个人觉得c++继承与多态这块仿佛掉进了泥潭,深陷其中无法自拔。现将部分迷茫的问题总结如下:
1.析构函数为什么要声明为虚函数?
基类的析构函数需要声明为虚函数:
当派生类对象经由基类指针删除时,而基类带着一个non_virtual析构函数,实际执行时发生的是对象的派生类对象成员没有被销毁。即局部销毁,会发生内存泄漏,故通常将基类析构函数声明为虚函数
代码实现为:
#include <iostream>
using namespace std;
#include <windows.h>
class A //基类
{
public:
A()//构造函数
:_a(1)
{
cout << "A()" << endl;
}
~A()//析构函数
{
cout << "~A()" << endl;
}
private:
int _a;
};
class B :public A//B继承A
{
public:
B()//构造函数
:_b(2)
{
cout << "B()" << endl;
}
~B()//析构函数
{
cout << "~B()" << endl;
}
private:
int _b;
};
int main()
{
A *p = new B;//基类指针指向子类对象
delete p;//删除指针
system("pause");
return 0;
}
结果:
由此可以发现最后只删除了A类的对象,而没有删除B类的,造成了局部销毁。
解决方案:将基类析构函数声明为virtual,此后删除派生类对象就会销毁整个对象,当然包括派生类成员。
解决部分代码:
//同上面代码区别是将A类的析构函数声明为虚函数
#include <iostream>
using namespace std;
#include <windows.h>
class A
{
public:
A()
:_a(1)
{
cout << "A()" << endl;
}
virtual ~A()//声明为virtual
{
cout << "~A()" << endl;
}
private:
int _a;
};
class B :public A
{
public:
B()
:_b(2)
{
cout << "B()" << endl;
}
~B()
{
cout << "~B()" << endl;
}
private:
int _b;
};
int main()
{
A *p = new B;
delete p;
system("pause");
return 0;
}
结果为:
以上方法解决了局部销毁问题。delete p
时经编译器特殊处理后调用destructor() +operator delete
从而删除指针p
注意:
析构函数经编译器处理后并非~A(),而是destructor,因此会对基类的析构函数构成隐藏,再者析构函数不能被继承。将基类的析构函数声明为虚函数后,子类对基类的析构函数构成重写,根据对象的指向,调用相应的析构函数。
2.不能被声明为虚函数的函数
1. 普通函数
普通函数只能被重载,不能被重写,声明为虚函数无任何意义,因为编译器会在编译时绑定函数。
2.构造函数
因为构造函数本身就是为了初始化对象成员。如果被声明为虚函数,就需要通过vtable虚基表调用,但此时对象还未实例化,找不到vtable,因此不能为虚函数。
从实现上看,vtable实在构造函数调用后才建立的,故构造函数不能为虚函数,同时编译时会报错。
3.内联函数
内联函数是在代码中直接展开,减少调用函数栈的开销,而虚函数是为了继承后对象能准确的执行自己的操作,而且内联函数在编译时展开,虚函数在运行时动态绑定。
4.静态成员函数
静态成员函数对于每个类来说只有一份,各个对象共享一份代码,它的实现不是为了构成多态,而是为了处理静态数据成员,没有动态绑定的必要性。
5.友元函数
友元函数不能被继承即若A为B的友元函数,C是B的友元函数,则不能说C是A的友元函数
。都没有继承特性,更谈不上虚函数的说法了!