在继承关系中,虚函数、虚析构函数、虚基类中使用的关键字virtual都是在告诉编译器,此处要进行特殊处理:
虚函数:函数重写时的要求编译器动态绑定来实现多多态 ;
虚析构函数:当基类指针指向在堆内实现的派生类的动态对象时,virtaul声明的基类析构函数是告诉编译器,还要调用派生类的析构函数。
虚基类:当派生类有多重继承和多继承时,在继承链上有两个子类继承自同一基类时,此两个子类作为基类再次派生出一个类,若只想保存这两个子类的基类的一个实例,需用virtual来声明继承关系,编译器对虚基类只实现一个实例,从而避免二义性。
1 虚函数
面向对象的在继承时要实现动态多态,需要用关键字virtual告诉编译器,由编译器在编译时生成一个虚函数表,程序运行时实现每个对象通过一个自动添加的函数指针来动态绑定一个函数去实现多态:
#includeclass CShape{public:virtual double Area(){cout<Area()<Area()<
2 虚析构函数
在实现多态时,如果是由基类指针指向在堆上动态创建派生对象时,如果delete此虚类指针,则基类的析构函数被调用,并不会调用派生类的析构函数,如果派生类中有在堆上动态创建的数据时,则会出现内存泄露。C++编译器的做法时,如果基类的析构函数前面有用virtual修饰,编译器会自动去调用派生类的析构函数(实现动态绑定,如果其它成员函数的动态绑定一样),做内存释放的工作。
如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,使析构函数为虚一般是个坏主意。因为它会为类增加一个虚函数表,使得对象的体积翻倍,还有可能降低其可移植性。实际上,很多人这样总结:当且仅当类里包含至少一个虚函数的时候才去声明虚析构函数。
虚析构函数是为了解决这样的一个问题:基类的指针指向派生类对象,并动态调用派生类的析构函数。
#include using namespace std;class CSon{public:~CSon(){cout<
3 虚基类
当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。
如果将这种多继承和多重继承画成图形关系,会有菱形的图案出现,称为菱形继承:
如图,子类D最后会接受分别来自B和C的同一个或多个相同拷贝,从而产生了多个拷贝,即不止一次的通过多个路径继承类在内存中创建了基类成员的多份拷贝。而这些是B和C从父类继承而来,所以D类该继承B还是C传下来的,还是都接受呢?这样就产生歧义(二义性),虚基类的基本原则是在内存中只有基类成员的一份拷贝。这样,通过把基类继承声明为虚拟的(virtual限定符),就只能继承基类的一份拷贝,从而消除歧义。
#includeclass base { protected: int a; public: base(){ cout <
输出结果为:0123,如果省略掉其中的关键字virtual,则输出01023,可以看出,构造函数有重复调用。
-End-