构造函数可以是虚函数吗?
构造函数不能为虚函数的理由:
1. 从存储空间角度
虚函数对应个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么着vtable呢?所以构造函数不能是虚函数。
2. 从使用角度
虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义。所以构造函数没有必要是虚函数。
虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。
3.构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它。但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。
4. 从实现上看,vbtable在构造函数调用后才建立,因而构造函数不可能成为虚函数。
从实际含义看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象的生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数。
5. 当一个构造函数被调用时,它做的首要的事情之一是初始化它的VPTR。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码,既不是基类,也不是为它的派生类。
所以它使用的VPTR必须是对于这个类的VTABLE。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内,VPTR将保持初始化为指向这个VTABLE,但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置VPTR指向它的VTABLE,等。直到最后的构造函数结束。VPTR的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用从基类到派生类的另一个理由。
但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置VPTR指向它自己的VTABLE。如果函数调用使用虚机制,它将产生通过它自己的VTABLE的调用,而不是最后的VTABLE(所有构造函数被调用后才会有最后的VTABLE)。
析构函数可以是虚函数吗?
析构函数可以是虚函数,而且经常被这样使用。
一 析构函数的调用规则:
1. 如果是从一个基类派生的,则调用它的析构函数,再调用它基类的析构函数,依此类推。
2. 自动对象(局部的)在离开作用域的时候自动调用析构函数。(确切的说是编译器在离
开对象作用域的时候添加析构函数的代码)
3. new出来的对象要有delete时才调用析构函数。
补充:
4. 基类指针可以指向派生类。
5. a.fun(); 调用的是对象a的fun();要打破这样的规则,要把函数设置为虚拟的.
6. delete b;调用的是b所属类的析构函数,要打破这样的规则,要把析构函数设置为虚拟的。
结论:在一个派生体系中,必须使所有的析构函数都被调用,这就要从最下面的析构函数开始执行
如:CBase->CDerived,必须从CDerived开始执行,CBase的析构函数才可以调用,如果从CBase开
始调用,则CDerived的析构函数得不到调用,这将导致解构的现象。
下面的例子中,delete b,由于b是基类的对象,则调用的是基类的析构函数(条款5),则派生类的
析构函数得不到调用(结论),所以要把基类的析构函数设置为虚拟的,这样delete b时调用的是派
生类的析构函数,接着调用基类的析构函数。
虚析构函数是为了解决这样的一个问题:
基类的指针指向派生类对象,并用基类的指针删除派生类对象