为什么存在虚析构函数?
Deleting an object through pointer to base invokes undefined behavior
unless the destructor in the base class is virtual:
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base {};
Base* b = new Derived;
delete b; // safe
A virtual destructor is required when:
- you invoke delete on a pointer to a derived object via a base class
1. 派生类的析构函数
派生类的析构函数不负责撤销基类对象的成员,编译器总是显式调用派生类对象基类部分的析构函数。
这句话的含义是:我们定义派生类的析构函数时,不用管基类部分的成员,只撤销派生类自己的成员即可(如果需要的话)。编译器会自己调用基类的析构函数。我们不用在派生类的析构函数中显示调用它。
另:不管我们定不定义自己的析构函数,编译器都会合成默认的版本;这一点与构造函数不同。
例子:
class Base4
{
public:
Base4(int ii, int jj) :i(ii), j(jj) { cout << "调用基类的一般构造函数" << endl; }//基类的一般构造函数
Base4() :i(0), j(5203132) { cout << "调用基类的默认构造函数" << endl; }//基类的默认构造函数
~Base4() { cout << "调用基类的析构函数" << endl; }
void show_base() { cout << i << "t" << j<<"t"; }
public:
int i;
private:
int j;
};
class D4 :public Base4
{
public:
D4() :k(0) { cout << "调用派生类的默认构造函数" << endl; }//派生类的默认构造函数
D4(int ii, int jj, int kk) :Base4(ii, jj), k(kk) { }//派生类的一般构造函数
D4(int kk) : k(kk) {}//派生类的一般构造函数,隐式调用基类的默认构造函数
~D4() { cout << "调用派生类的析构函数" << endl; }
int k;
void show_d() { show_base(); cout << k << endl; }
};
//主函数
int main()
{
/*******************************************************************/
// 派生类的析构函数
/*******************************************************************/
cout << "D4 *pD4 = new D4:" << endl;
D4 *pD4 = new D4;
cout << "delete pD4:" << endl;
delete pD4;
system("pause");
return 0;
}
执行结果:

从上面我们看到,当我们delete pD4的时候,先调用了派生类的析构函数,然后又自动调用了基类的析构函数。
2. 虚析构函数
处理继承层次中的对象时,指针的静态类型可能与被删除对象的动态类型不同,可能会删除实际指向派生类对象的基类类型指针,此种行为是未定义的。
类型定义不变,修改主函数为如下所示:
int main()
{
/*******************************************************************/
// 派生类的析构函数
/*******************************************************************/
Base4 *pB4 = new D4;
delete pB4;
system("pause");
return 0;
}
一个指向基类的指针,实际指向了一个派生类,如此调用delete结果会如何呢?

很明显,只有基类的析构函数被调用,派生类的析构函数未被调用。如果派生类中新增了动态分配的成员,则这部分成员的内存未被释放**,发生内存泄露**。
但是,基类指针指向派生类对象的情况又是经常遇到,不可避免的。所以应该怎样处理这种情况?
答案就是在基类中定义virtual 析构函数。
我们将基类定义中的析构函数改为virtual析构函数,其他不变:
class Base4
{
public:
Base4(int ii, int jj) :i(ii), j(jj) { cout << "调用基类的一般构造函数" << endl; }//基类的一般构造函数
Base4() :i(0), j(5203132) { cout << "调用基类的默认构造函数" << endl; }//基类的默认构造函数
virtual ~Base4() { cout << "调用基类的析构函数" << endl; }
void show_base() { cout << i << "t" << j<<"t"; }
public:
int i;
private:
int j;
};
同样使用上一个例子中的主函数,执行结果如下:

显然,派生类和基类的析构函数都被调用了,不会有内存泄漏发生。
3. 其他注意项
如果继承层次中根类的析构函数为虚函数,则派生类析构函数也是虚函数,不论是自己定义的还是合成的;
复制控制成员3法则:如果类需要析构函数,则它也需要赋值操作符和复制构造函数。
但是,基类的虚析构函数是三法则的一个重要例外:基类几乎总是需要虚析构函数,但并不一定需要赋值操作符和复制构造函数。
构造函数和赋值操作符不是虚函数。
面试实战问题:
构造函数和析构函数可以是虚函数吗?

972

被折叠的 条评论
为什么被折叠?



