问:virtual function的优缺点
优点:实现多态
缺点:MFC中的消息机制以及Qt中都没有采用C++中的虚函数机制,原因大概如下~
1.在子类很少覆盖基类函数实现的时候内存开销太大,每个类需要产生一张虚表,每生成一个对象的时候,需要在对象里存放一个vptr。
2.基类指针指向派生类对象的情况下,不方便内联优化(有些情况可以内联,参考我前面的文章)
3.在执行效率上肯定多了一些开销,需要寻找函数地址
4.虚表的存在可能破坏一些封装安全,可以通过vptr绕过private的限制
问:多继承的优缺点
好处:简单来讲就是为了实现多个基类特有的功能
缺点:菱形继承;二义性(可通过虚继承解决)
以下代码出现菱形继承
class A
{
public:
int _a;
};
class B:public A
{
public:
int _b;
};
class C:public A
{
public:
int _c;
};
class D:public B,public C
{
public:
int _d;
};
int main()
{
D d;
d._a = 10;//这里调用成员的时候就会出现错误,对_a的访问不明确
d._b = 2;
d._c = 3;
d._d = 4;
return 0;
}
以下代码可解决报错,但依旧冗余
d.B::_a = 10;
d.C::_a = 20;
使用虚继承解决
class B:virtual public A
class C:virtual public A
总结:
1.虚继承解决了在菱形继承中子类对象包含多分父类对象的数据冗余和浪费空间的问题
2.虚继承体系看起来很复杂,在实际应用中我们通常不会定义如此复杂的集成体系。一般不到万不得已不要使用菱形虚拟继承,因为它解决数据冗余问题的同时也带来了性能上的损耗
问:为什么要用virtual destructor?
为了能正常调用子类的析构函数,否则会出现只调用基类析构函数而没有调用子类析构函数情况。(如果子类有指针成员且申请了堆空间,不调用子类调用析构函数就会造成内存泄漏)
class Base
{
public:
virtual ~Base()
{
cout<<"delete Base"<<endl;
};
};
class Derive : public Base
{
public:
int* p;
Derive()
{
p = new int;//构造时申请空间
}
~Derive()
{
delete p;//析构时释放空间
cout<<"delete Derive"<<endl;
};
};
int main()
{
Base *pbase = new Derive;
delete pbase ;
//如果析构函数不是virtual,就只会调用当前指针类型的析构函数(也就是基类Base的析构函数)。
//反之,则会根据虚表找到派生类的析构函数并调用。
//由于C++语言机制(或者说编译器会插入一些额外的代码),调用完子类的析构函数后会自动调用基类的析构函数
return 0;
}
问:为什么没有virtual constructor?
第一点,构造函数执行前,对象的内存信息都没有,vptr也没有初始化,如何找到虚函数表,实现虚调用。其次,需要理解虚函数的意义,他是为了实现多态,让子类去覆盖父类的操作,但是前提是你需要知道当前的类型是什么,而C++里面构造函数可以说决定了他的类型。另外,构造函数与析构函数的执行不同,他是从父类开始一步一步的构造成一个子类(即使父类没有构造函数也可能会被编译器合成一个),所以你不能跳过父类的构造函数,子类的很多成员可能需要在父类的基础上去初始化。
不过,从设计的角度来考虑的话,虚构造的思想还是有意义的,和我们设计模式中的工厂模式有点类似,根据不同的类型去做不同的初始化。Delphi语言因为有一个祖先基类,所以可以做到虚构造,而C++从语言层面来讲不存在真正意义上的虚构造函数。
问:哪些函数不能是虚函数?