虚拟构造函数是指能够根据输入给它的数据的不同而建立不同类型的对象。虚拟构造函数在很多场合下都有用处,从磁盘(或者通过网络连接,或者从磁带机上)读取对象信息只是其中的一个应用。(WQ加注:readComponent()的实现可详见《汤姆.斯旺C++编程秘诀》)
还有一种特殊种类的虚拟构造函数――虚拟拷贝构造函数――也有着广泛的用途。虚拟拷贝构造函数能返回一个指针,指向调用该函数的对象的新拷贝。因为这种行为特性,虚拟拷贝构造函数的名字一般都是copySelf,cloneSelf或者是象下面这样就叫做clone。
正如我们看到的,类的虚拟拷贝构造函数只是调用它们真正的拷贝构造函数。因此“拷贝”的含义与真正的拷贝构造函数相同。如果真正的拷贝构造函数只做了简单的拷贝,那么虚拟拷贝构造函数也做简单的拷贝。如果真正的拷贝构造函数做了全面的拷贝,那么虚拟拷贝构造函数也做全面的拷贝。如果真正的拷贝构造函数做一些奇特的事情,象引用计数或copy-on-write(参见条款M29),那么虚拟构造函数也这么做。完全一致,太棒了。
注意上述代码的实现利用了最近才被采纳的较宽松的虚拟函数返回值类型规则。被派生类重定义的虚拟函数不用必须与基类的虚拟函数具有一样的返回类型。
在NLComponent中的虚拟拷贝构造函数能让实现NewLetter的(正常的)拷贝构造函数变得很容易:
class NewsLetter {
public:
NewsLetter(const NewsLetter& rhs);
...
private:
list<NLComponent*> components;
};
NewsLetter::NewsLetter(const NewsLetter& rhs)
{
// 遍历整个rhs链表,使用每个元素的虚拟拷贝构造函数
// 把元素拷贝进这个对象的component链表。
// 有关下面代码如何运行的详细情况,请参见条款M35.
for (list<NLComponent*>::const_iterator it =
rhs.components.begin();
it != rhs.components.end();
++it) {
// "it" 指向rhs.components的当前元素,调用元素的clone函数,
// 得到该元素的一个拷贝,并把该拷贝放到
// 这个对象的component链表的尾端。
components.push_back((*it)->clone());
}
}
如果你对标准模板库(STL)不熟悉,这段代码可能有些令人费解,不过原理很简单:遍历被拷贝的NewsLetter对象中的整个component链表,调用链表内每个元素对象的虚拟构造函数。我们在这里需要一个虚拟构造函数,因为链表中包含指向NLComponent对象的指针,但是我们知道其实每一个指针不是指向TextBlock对象就是指向Graphic对象。无论它指向谁,我们都想进行正确的拷贝操作,虚拟构造函数能够为我们做到这点。
虚拟化非成员函数
就象构造函数不能真的成为虚拟函数一样,非成员函数也不能成为真正的虚拟函数(参见Effective C++ 条款19)。然而,既然一个函数能够构造出不同类型的新对象是可以理解的,那么同样也存在这样的非成员函数,可以根据参数的不同动态类型而其行为特性也不同。
具有虚拟行为的非成员函数很简单。你编写一个虚拟函数来完成工作,然后再写一个非虚拟函数,它什么也不做只是调用这个虚拟函数。为了避免这个句法花招引起函数调用开销,你当然可以内联这个非虚拟函数(参见Effective C++ 条款33)。