1.构造函数能否声明为虚函数: 构造函数不能声明为虚函数,虚函数对应一个vptr,可是这个vptr其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vptr来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vptr呢?所以构造函数不能是虚函数。(而且现在VS的编译器一般也会报错)。
2.析构函数能否声明为虚函数: 析构函数可以声明为虚函数,而且有时是必须声明为虚函数,是因为删除指向派生类的基类指针的时候,如果析构函数不是虚函数,派生类的析构函数将不会被执行,直接后果的内存泄漏!!!
3.能否在构造函数里面调用虚函数:编译不会报错,但是最好不要这样做!在调用构造这时候类里面的变量可能还被未初始化, 虚函数这时可能会访问内存中未知的区域,比较容易出错,所以最好不要在构造函数里调用虚函数。
4.能否在析构函数里面调用虚函数:同理,编译不会报错,但是最好不要这样做!在调用析构的时候类里面的变量可能已经被析构, 虚函数这时可能会访问内存中未知的区域,比较容易出错,所以最好不要在析构函数里调用虚函数。
不建议在构造函数和析构函数里面调用虚函数。
构造函数不能声明为虚函数的原因是:
解释一:所谓虚函数就是多态情况下只执行一个。而从继承的概念来讲,总是要先构造父类对象,然后才能是子类对象。如果构造函数设为虚函数,那么当你在构造父类的构造函数时就不得不显示的调用构造。还有一个原因就是为了防错,试想如果你在子类中一不小心重写了个跟父类构造函数一样的函数,那么你的父类的构造函数将被覆盖,也即不能完成父类的构造.就会出错。
解释二:虚函数的主要意义在于被派生类继承从而产生多态。派生类的构造函数中,编译器会加入构造基类的代码,如果基类的构造函数用到参数,则派生类在其构造函数的初始化列表中必须为基类给出参数,就是这个原因。
虚函数的意思就是开启动态绑定,程序会根据对象的动态类型来选择要调用的方法。然而在构造函数运行的时候,这个对象的动态类型还不完整,没有办法确定它到底是什么类型,故构造函数不能动态绑定。(动态绑定是根据对象的动态类型而不是函数名,在调用构造函数之前,这个对象根本就不存在,它怎么动态绑定?)
编译器在调用基类的构造函数的时候并不知道你要构造的是一个基类的对象还是一个派生类的对象。
析构函数设为虚函数的作用:
解释:在类的继承中,如果有基类指针指向派生类,那么用基类指针delete时,如果不定义成虚函数,派生类中派生的那部分无法析构。
#include<stdio.h> #include<iostream> class A{ public:A(); virtual~A(); void fun1(){ printf("123"); } }; A::A(){} A::~A(){ printf("Delete class A\n"); } class B : public A { public:B(); ~B(); void fun2(){ printf("123456"); } }; B::B(){ } B::~B(){ printf("Delete class B\n"); } class C : public B { public:C(); ~C(); void fun2(){ printf("123456"); } }; C::C(){ } C::~C(){ printf("Delete class C\n"); } A *a=new C; int main(){ delete a; return 0; }