一. 设置虚函数需要考虑五个方面
1. 只有类的成员函数才能声明为虚函数
2. 类的静态成员函数不能为虚函数,因为调用静态成员函数不需要实例只需要用类名即可。而调用一个虚函数需要类的实例化对象,因为需要从实例化对象中的指向虚函数表的指针得到虚函数的地址,所以静态成员函数是不能为虚函数的
3. 内联函数不能为虚函数。
因为内联函数是在编译的时候决定要不要内联的,内联函数原理是在程序中有用到内联函数的地方直接把程序代码插入,这样省去了函数调用的时间开销,提高了效率。
虚函数是在运行的时候动态的决定要调用哪个函数,因此内联函数是不能为虚函数的。
4. 构造函数不能为虚函数
构造函数是在构造一个对象之前调用的,我们知道调用虚函数是需要类的实例化对象中指向虚函数表的指针的,但是构造函数在此时是没有构造出对象的,因此两者之间矛盾,所以构造函数是不能为虚函数的。
虽然构造函数不能为虚函数,但是构造函数可以调用虚函数,例子如下
#include<iostream> #include<algorithm> using namespace std; //类A class A{ public: A(){ Print(); } //类A的虚函数 virtual void Print(void){ cout<<"class A"<<endl; } private: }; //类B继承类A class B:public A{ public: B(){} //类B中重写A的虚函数 virtual void Print(void){ cout<<"class B"<<endl; } private: }; int main(){ //test A *a = new B(); a->Print(); getchar(); return 0; } /* 程序输出: class A class B 解释如下: 1. main函数中基类指针a生成了一个子类对象,这个时候会先调用基类的构造函数 再调用子类的构造函数,因为在基类A构造函数中调用了虚函数所以输出了class A 2. 基类指针调用Print函数,这个时候调用的是子类的函数,输出class B */
5. 析构函数可以为虚函数,一般情况下基类的析构函数声明为虚函数
因为当基类指针指向子类对象的时候,如果这个时候delete基类指针,如果基类的析构函数是虚函数那么会先去执行子类的虚构函数再执行基类的析构函数;如果基类的析构函数不是虚函数则只会执行基类的析构函数,这样可能造成子类对象销毁不完全,可能有内存泄漏问题
例子如下
【基类析构函数是虚函数】
#include<iostream> #include<algorithm> using namespace std; //类A class A{ public: virtual ~A(void){ cout<<"~A()"<<endl; } private: }; //类B继承类A class B:public A{ public: ~B(void){ cout<<"~B()"<<endl; } private: }; int main(){ //test A *a = new B(); delete a; a = NULL; getchar(); return 0; } /* 输出: ~B() ~A() 解释如下: 1. 基类A的析构函数为虚函数 2. main函数中基类指针生成了一个子类B的对象,然后delete基类指针a 由于基类析构函数是虚函数,因此会先执行子类B的析构函数再执行基类 A的析构函数 */
【基类析构函数不是虚函数】
#include<iostream> #include<algorithm> using namespace std; //类A class A{ public: ~A(void){ cout<<"~A()"<<endl; } private: }; //类B继承类A class B:public A{ public: ~B(void){ cout<<"~B()"<<endl; } private: }; int main(){ //test A *a = new B(); delete a; a = NULL; getchar(); return 0; } /* 输出: ~A() 解释如下: 1. 基类A的析构函数不是虚函数 2. main函数中基类指针生成了一个子类B的对象,然后delete基类指针a 由于基类析构函数不是虚函数,因此只会执行基类A的析构函数 */