3.2 构造函数和析构函数中的虚函数调用

  一个类的虚函数在它自己的构造函数和析构函数中被调用的时候,它们变成普通函数了,不“虚”了。也是说不能在构造函数和析构函数中让自己“多态”。例如:

class A
{
public:
A() { foo();} // 在这里,无论如何都是A::foo()被调用!
~A() { foo();} // 同上
virtual void foo();
};
class B: public A
{
public:
virtual void foo();
};
void bar()
{
A * a = new B;
delete a;
}

  如果你希望delete a的时候,会导致B::foo()被调用,那么你错了。同样,在new B的时候,A的构造函数被调用,但是在A的构造函数中,被调用的是A::foo()而不是B::foo()。

  3.3 多继承中的虚函数

  继承的子类中只有一个虚函数指针,来访问所有的虚函数(包括父类和自己的)。无论继承的虚类(抽象类)有多少,都只保持一个虚函数指针,但虚拟继承有些实现不同。其实子类的这个指针是继承父类的虚函数指针,只不过是每个类的虚函数表不一样,所以子类的虚函数指针指向的表不一样。子类的虚函数表包括继承、覆盖和新生的虚函数。

  3.4 什么时候使用虚函数

  在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它应该是虚的。从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现。通过这样的方法,可以将对象的行为抽象化。

  以设计模式[2]中Factory Method模式为例,Creator的factoryMethod()是虚函数,派生类override这个函数后,产生不同的Product类,被产生的Product类被基类的AnOperation()函数使用。基类的AnOperation()函数针对Product类进行操作,当然Product类一定也有多态(虚函数)。

  另外一个例子是集合操作,假设你有一个以A类为基类的类层次,又用了一个std::vector[url=]来保存这个类层次中不同类的实例指针,那么你一定希望在对这个集合中的类进行操作的时候,不要把每个指针再cast回到它原来的类型(派生类),而是希望对他们进行同样的操作。那么应该将这个“一样的操作”声明为virtual。

  现实中,远不只我举的这两个例子,但是大的原则都是我前面说到的“如果发现一个函数需要在派生类里有不同的表现,那么它应该是虚的”。这句话也可以反过来说:“如果你发现基类提供了虚函数,那么你好override它”。