<<C++ Primer Plus>>Fifth Edition
1.构造函数
构造函数不能是虚函数。创建派生类对象时,将调用派生类的构造函数,而不是基类的构造函数,然后,派生类的构造函数将使用基类的一个构造函数,这种顺序不同于继承机制。因此,派生类不继承基类的构造函数,所以将类构造函数声明为虚拟的没有什么意义.
2.析构函数
析构函数应当是虚函数,除非类不用做基类.例如,假设Employee是基类,Singer是派生类,并添加一个char*成员,该成员指向由new分配的内存.当Singer对象过期时,必须调用~Singer()析构函数来释放内存.
请看下面代码:
Employee *pe = new Singer; // legal because Employee is base for Singer
.........
delete pe; // ~Employee() or ~Singer()?
如果使用默认的静态联编,delete语句将调用~Employee()析构函数.这将释放由Singer对象中的Employee部分指向的内存,但不会释放新的类成员指向的内存.但如果析构函数是虚拟的,则上述代码将先调用~Singer()析构函数释放由Singer组件指向的内存,然后,调用~Employee()析构函数来释放由Employee组件指向的内存.
这意味着,即使基类不需要显示析构函数提供服务,也不应依赖于默认构造函数,而应提供虚拟析构函数,即使它不执行任何操作:
virtual ~BaseClass(){}
3. 友元
友元不能是虚函数,因为友元不是类成员,而只有成员才能是虚函数.如果说由于这个原因引起了设计问题,可以通过让友元函数使用虚拟成员函数来解决.
4.没有重新定义
如果派生类没有重新定义函数,将使用该函数的基类版本.如果派生类位于派生链中,则将使用最新的虚函数版本,例外的情况是基类版本是隐藏的.
5.重新定义隐藏方法
假设创建了如下所示的代码:
class Dwelling
{
public:
virtual void showperks(int a) const;
.....
};
class Hovel: public Dwelling
{
public:
virtual void showperks() const;
........
};
这将导致问题,可能会出现类似于下面这样的编译器警告:
Warning: Hovel :: showperks(void) hides Dwelling::showperks(int)
也可能不会出现警告.但结果怎么样,代码将具有如下含义:
Hovel trump;
trump.showperks(); // valid
trump.showperks(5); //invalid
新定义将showperks()定义一个不接受任何参数的函数.重新定义不会生成函数的两个重载版本,而是隐藏了接受一个int参数的基类版本.简而言之,重新定义继承的方法并不是重载.如果在派生类中重新定义函数,将不是使用相同的函数特征覆盖基类声明,而是隐藏同名的基类方法,不管参数特标如何.
这引出两条经验规则:第一,如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针(这种例外是新出现的).这种特征被称为返回类型协变(covariance of return type),因为允许返回类型随类类型的变化而变化:
class Dwelling
{
public:
// a base method
virtual Dwelling &build(int n);
......
};
class Hovel : public Dwelling
{
public:
// a derived method with a covariant return type
virtual Hovel &build(int n); // same function signature
.........
};
注意,这种意外只适用返回值,而不适用于参数
第二,如果基类声明被重载了,则应在派生类中重新定义所有的基类版本.
class Dwelling
{
public:
//three overloaded showperks()
virtual void showperks (int a) const;
virtual void showperks (double x) const;
virtual void showperks() const;
........
};
class Hovel: public Dwelling
{
public:
// three redefined showperks()
virtual void showperks(int a) const;
virtual void showperks(double x) const;
virtual void showperks() const;
..........
};
如果只重新定义一个版本,则另外两个版本将被隐藏,派生类对象将无法使用它们.注意,如果不需要修改,则新定义可只调用基类版本.