【1】 构造函数为什么不能是虚函数?
虚函数有什么缺点(有的会问,析构函数声明为虚函数有什么缺点)?
大体原因如下:如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,使析构函数为虚一般是个坏主意。因为它会为类增加一个虚指针、一个虚函数表,使得对象的体积翻倍,并会降低其可移植性。
所以基本的一条是:无故的声明虚析构函数和永远不去声明一样是错误的。实际上,很多人这样总结:当且仅当类里包含至少一个虚函数的时候才去声明虚析构函数。
虚函数的实现要求对象携带额外的信息,这些信息用于在运行时确定该对象应该调用哪一个虚函数。典型情况下,这一信息具有一种被称为 vptr(virtual table pointer,虚函数表指针)的指针的形式。vptr 指向一个被称为 vtbl(virtual table,虚函数表)的函数指针数组,每一个包含虚函数的类都关联到 vtbl。当一个对象调用了虚函数,实际的被调用函数通过下面的步骤确定:找到对象的 vptr 指向的 vtbl,然后在 vtbl 中寻找合适的函数指针。
虚函数如何被实现的细节是不重要的。重要的是如果 Point 类包含一个虚函数,这个类型的对象的大小就会增加。在一个 32 位架构中,它们将从 64 位(相当于两个 int)长到 96 位(两个 int 加上 vptr);在一个 64 位架构中,他们可能从 64 位长到 128 位,因为在这样的架构中指针的大小是 64 位的。为 Point 加上 vptr 将会使它的大小增长 50-100%!Point 对象不再适合 64 位寄存器。而且,Point 对象在 C++ 和其他语言(比如 C)中,看起来不再具有相同的结构,因为其它语言缺乏 vptr 的对应物。结果,Points 不再可能传入其它语言写成的函数或从其中传出,除非你为 vptr 做出明确的对应,而这是它自己的实现细节并因此失去可移植性。
STL里的容器都没有虚析构函数!
因为 C++ 规定:用不带有虚析构函数的基类的指针来删除一个派生类,其结果是未定的。
时时刻刻让自己记住
l 应该为多态基类声明虚拟析构函数。如果一个类有一个虚函数,那么它也应该有一个虚析构函数
l 如果一个类不是被设计为基类或者它们并不是按照多态的方式来使用的,不要为它们声明虚析构函数
有时候,把析构函数设定为 pure virtual 是非常方便的。一个 pure virtual 函数可以让一个类成为抽象类。有时,你可能需要让你的类成为一个 abstract class ,但是你一时又找不到合适的纯虚函数。怎么办呢?因为一个抽象类往往是要被作为基类的,而一个基类往往又应该有一个虚析构函数。这样一来:声明一个 pure virtual destructor 就是一个不错的主意。一箭双雕。
class AWOV { // AWOV = "Abstract w/o Virtuals"
public :
virtual ~AWOV() = 0; // declare pure virtual destructor
};
这个类有一个纯虚函数,因此这是以个抽象基类,并且这个类有一个虚析构函数,这也使你远离了析构函数的问题,唯一要注意的,就是一定要为纯虚析构函数提供一份实现。
【4】一个只能在堆内存上实例化的类和一个只能在栈内存上实例化的类
只能在堆内存上实例化的类:将析构函数定义为private,在栈上不能自动调用析构函数,只能手动调用。也可以将构造函数定义为private,但这样需要手动写一个函数实现对象的构造。
只能在栈内存上实例化的类:将函数operator new和operator delete定义为private,这样使用new操作符创建对象时候,无法调用operator new,delete销毁对象也无法调用operator delete。