C++ View 第 4 期
第
第
4
4
期
期
目
目
录
录
2
2
0
0
0
0
1
1
年
年
1
1
0
0
月
月
焦点
F
o
c
u
s
Andrei Alexandrescu 介绍
03
回音壁
59
特稿
F
e
a
t
u
r
e
虚函数是否应该仅被声明为 private/protected
04
专栏
C
o
l
u
m
n
G
G
e
e
n
n
e
e
r
r
i
i
c
c
<
<
P
P
r
r
o
o
g
g
r
r
a
a
m
m
m
m
i
i
n
n
g
g
>
>
类型和数值间的映射
09
设
设
计
计
笔
笔
记
记
依赖倒置原则 DIP
23
C
C
+
+
+
+
批
批
评
评
系
系
列
列
函数重载
20
P
P
a
a
t
t
t
t
e
e
r
r
n
n
H
H
a
a
t
t
c
c
h
h
i
i
n
n
g
g
弃儿、养子和替身
34
模
模
式
式
罗
罗
汉
汉
拳
拳
42
Interface(接口)模式
天
天
方
方
夜
夜
谭
谭
V
V
C
C
L
L
多态
47
主
主
编
编
:
:
王
王
曦
曦
封
封
面
面
设
设
计
计
:
:
棒
棒
棒
棒
虎
虎
本
本
期
期
编
编
辑
辑
:
:
p
p
l
l
p
p
l
l
i
i
u
u
l
l
y
y
主
页
:
h
t
t
p
:
/
/
c
p
p
v
i
e
w
y
e
a
h
.
n
e
t
h
t
t
p
:
/
/
c
p
p
v
i
e
w
y
3
6
5
.
c
o
m
电
邮
:
c
p
p
v
i
e
w
@
s
o
h
u
.
c
o
m
导
导
读
读
我想我要是再不把 C++ View 第 4 期交出来,虫虫
真要跟我急了。想他原本出于对朋友的信任,委托我在
他开学无暇之际接手第 4 期的编务工作,每次他向我询
问杂志进展,我都一次比一次多一些愧疚地说“就快差
不多了”,没料到“差不多”的一点让他等的竟是如此
漫长,连热心的读者朋友都等得不耐烦了,何况他这个
主编呢。说来的确惭愧,虫虫将文章都已经约好了,封
面也准备了,前几期的版面模板都给我了,我几乎什么
都不需要做就可以过一把编辑瘾。尽管我也努了力,但
工作上的事情几乎占用了我所有的时间,加上我做事效
率低下,最终使得我的这份“答卷”确实有些让人失望。
说这些当然不是想把我向虫虫的“忏悔书”贴在这
儿作导读。一来我是向大家解释第 4 期 C++ View 为何
姗姗来迟,并向关心我们杂志的朋友真诚地道歉;二来,
我想以我的经历顺便告诉读者朋友们,要做好一期这样
的杂志的确不容易,它涉及更多的不是技术上的工作,
而是一些诸如翻译的遣词用句、字体格式、排版效果的
细节小事。尽管这是一份免费杂志,但虫虫做事的认真
和要求的严格令我不敢怠慢(尽管结果并不令人满意)。
这份杂志虽然是虫虫一手创办的,但是我觉得它是我们
所有喜欢 C++和程序设计的人所组成的整个社区的,
因此,我希望大家能够共同出力来做好这份杂志,毕竟
一两个人的精力和时间是有限的。我真心地期待有能力
的朋友毛遂自荐,为这份杂志作一些具体的事情。
文章结束之前不能全是废话,我得向大家介绍完这
期的文章才算最后完成虫虫交给我的任务:这期文章不
多,但不乏精彩之篇。虫虫亲自捉笔的“天方夜谭 VCL”
第二篇给我们讲述 VCL 中的多态的实现,文笔很好;
另外一篇原创专栏“模式罗汉拳”的主人透明这次为我
们讲解“接口”这个看似简单实则“玄机重重”的模式;
yefeng 继续翻译 Andrei 的 Generic<Programming>系列,
模板发烧友不可错过;本期翻译的 John Vlissides 的
“Pattern Hatching”第二篇用的名字颇为吸引人,内容
当然也是可圈可点;还有译自 Robert Martin 的“设计
笔记”系列的 DIP 原则的介绍也许会让你有所感悟。
焦
焦
点
点
F
F
o
o
c
c
u
u
s
s
特
特
稿
稿
F
F
e
e
a
a
t
t
u
u
r
r
e
e
专
专
栏
栏
C
C
o
o
l
l
u
u
m
m
n
n
主
主
页
页
:
:
h
h
t
t
t
t
p
p
:
:
/
/
/
/
c
c
p
p
p
p
v
v
i
i
e
e
w
w
.
.
.
y
y
e
e
a
a
h
h
.
.
n
n
e
e
t
t
h
h
t
t
t
t
p
p
:
:
/
/
/
/
c
c
p
p
p
p
v
v
i
i
e
e
w
w
.
.
.
y
y
3
3
6
6
5
5
.
.
c
c
o
o
m
m
电
电
邮
邮
:
:
c
c
p
p
p
p
v
v
i
i
e
e
w
w
@
@
s
s
o
o
h
h
u
u
.
.
c
c
o
o
m
m
2
C++ View 第 4 期
Andrei Alexandrescu 介绍
Andrei Alexandrescu 博士毕业
于座落于华盛顿州西雅图的华盛
顿大学,并在那儿获得了 Ph.D.学
位。他是泛型编程方面的重量级作
品 Modern C++ Design: Generic
Programming and Design Patterns
Applied 一书的作者。该书在 2001
年 3 月由美国 Addsion-Wesley 出
版,中文繁体版和简体版都在翻译
过程之中(繁体版由台湾著名技术
作家侯捷先生和大陆的孟岩先生
合译,简体版正由清华出版社委托
北京大学潘爱民先生翻译)。
Andrei Alexandrescu 是 C/C++ User Journal 杂志的专栏作家。他经常在世界各地的学术会议上发
表论文演讲,比如 2001 年春天在英国举行的 ACCU(the Association of C&C++ Users)大会,在美国
举行的 OOPSLA ( ACM Conference on Object Oriented Programming, System, Language, and
Application) 2000,在意大利举行的 Extreme Programming 2000 会议。Andrei 也是 C++研讨会(The
C++ Seminar)的主要授课人之一,其他的主要授课人包括 Scott Meyers、Herb Sutter、Dan Saks、Steve
ewhurst 等。
关于 Modern C++ Design: Generic Programming and
Design Patterns Applied 一书,Design Patterns(《设计模式》)
的作者之一 John Vissides 在他为此书作的序中写到:该书所
描述的内容融会了泛型程序设计、template
metaprogramming、OOP、设计模式等诸多程序设计技术。它
为 C++的程序设计展现了一道崭新的风景线。无论是对编
程,还是对软件设计本身,甚至是软件分析和架构,都带来
了崭新的理念。
3
C++ View 第 4 期
虚拟函数是否应该仅被声明为 private/protected?
Cber
问题导入
我想对于大家来说,虚拟函数并不能算是个陌生的概念吧。至于怎么样使用它,大部分人都会
告诉我:通过在子类中重写(override)基类中的虚拟函数,就可以达到 OO 中的一个重要特性——多
态(polymorphism)。不错,虚拟函数的作用也正是如此。但如果我要你说一说虚拟函数被声明为 public
和被声明为 private/protected 之间的区别的话,你又是否还能象先前一样肯定地告诉我答案呢?
其实在一开始,我和大家一样,都喜欢把虚拟函数声明为 public(我并没有做太多的调查就说
了这些,因为我身边的程序员们几乎都是这样做的)。这样做的好处很明显:我们可以轻而易举地在
客户端(client,相对于 server,server 指的是我们所使用的继承体系架构,client 指的就是调用该体
系中方法/函数的外部代码)调用它,而不是通过利用那些烦人的 using 声明,或是强加给类的 friend
关系来满足编译器的 access 需求。OK,这是一个很不错的做法,简单、并且还能达到我们的要求。
但根据 OO 三大特性中的另一个特性——封装(encapsulation)来说(另一个就是继承),需要
我们将界面(interface)与实作(implementation)分开,即向外表现为一个通用的界面,而把实作
上的细节封装在模块内不让 client 端知晓。界面与实作的分离,使得我们得以设计出耦合度更低、
扩展性更好的系统来,并且还可以从这样的系统中提取出更多的可重用(reusable)的设计。
对于 OO 来说,封装是它的头等大事,享有最高的权利,其他的设计如果和它有着冲突,则以
符合它的设计为准。这样,问题就出来了,万一我们所希望出现的多态正好是具体的实作细节并且
我们不希望把它暴露给 client 端的话,那我们应该怎么样改动我们的设计以使得它能够适应封装的
需求呢?
可行的解决办法
幸好,C++中不但支持 public 的虚拟函数,也有着 private/protected 虚拟函数(在此我不想对于
public 和 private/protected 之间的区别多说)。前者是我们常用的形式,我也不多说,我们在此主要关
心的是 private/protected 的虚拟函数。
你可能会有疑惑,既然虚拟函数被声明为 private(protected 不算,因为子类可以直接访问基类
的 protected 成员),那子类中怎么还能对它进行重写呢?在此,你的疑虑是多余的,C++标准(也称
ISO 14882)告诉我们,虚拟函数的重写与它的具体存储权限没有任何关系,即便是声明为 private
4
C++ View 第 4 期
的虚拟函数,在子类中我们也同样可以重写它。因此,碰到上面所说的问题,我们就可以得到如下
的设计:
class Base {
public:
void do_something()
{
//......
really_do_something();
//......
}
private:
virtual void really_do_something()
{
//do the polymorphism code here
}
};
class Derived: public Base {
private:
void really_do_something()
{
//do the polymorphism code here
}
};
如果我们需要从上面的设计中得到实际上的多态行为,只要象下面一样调用 do_something
就可以了:
//client code
Base& b; //or Base* pb;
b.do_something(); //or pb->do_something();
这样我们就得以解决了在开始处提出的那个问题。
问题引申
那就这样完结了吗?没有。相反,至此我们才开始进行我们今天的讨论。首先让我们来看看多
态的实现:
void Base::do_something()
5