虚析构函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liushall/article/details/79947867

需要虚析构函数的原因:

首先看一下这段代码:

#include <iostream>
using namespace std;

class A {
private: 
    int *a;
public: 
    A() { a = new int; cout << "A::A() is called.\n"; }
    ~A() { delete a; cout << "A::~A() is called.\n"; }
    void print() { cout << "A::print() is called.\n"; }
};

class B : public A {
private: 
    int *b;
public:
    B() { b = new int; cout << "B::B() is called.\n"; }
    ~B() { delete b; cout << "B::~B() is called.\n"; }
    void print() { cout << "B::print() is called.\n"; }
};

int main() {
    A * p = new B();
    delete p;

    return 0;
}

说明:

  1. 可以将子类指针(或引用)赋值给父类指针(或引用)
  2. 在构造子类对象时,会先构造父类对象,再构造对应的子类对象部分
  3. new新空间时,之后必须进行delete,否则会造成内存泄漏

代码运行结果:

A::A() is called.
B::B() is called.
A::~A() is called.

可以发现仅对B进行了构造,而没有进行析构,因此可能会造成内存泄漏。为了避免这一问题的发生,就需要引出一个新的知识点——虚析构函数。

虚析构函数

析构函数虚函数我们已经熟悉了,那么如何实现虚析构函数呢?顾名思义,只要把虚函数与虚构函数结合起来就行了。例如:

class A {
public: 
    A();  //构造函数
    virtual ~A();  //虚析构函数
    virtual void print();  //虚函数
};

可见,只要在原来的析构函数声明前加上virtual关键字就形成了虚析构函数。由此可以修改代码为:

#include <iostream>
using namespace std;

class A {
private: 
    int *a;
public: 
    A() { a = new int; cout << "A::A() is called.\n"; }
    virtual ~A() { delete a; cout << "A::~A() is called.\n"; }  //添加virtual关键字,形成虚析构函数
    void print() { cout << "A::print() is called.\n"; }
};

class B : public A {
private: 
    int *b;
public:
    B( int *t ) { b = new int; cout << "B::B() is called.\n"; }
    ~B() { delete b; cout << "B::~B() is called.\n"; }
    void print() { cout << "B::print() is called.\n"; }
};

int main() {
    int x = 1;
    A * p = new B( &x );
    delete p;

    return 0;
}

运行结果:

A::A() is called.
B::B() is called.
B::~B() is called.
A::~A() is called.

此时各个对象都会调用析构函数,问题也就解决了。

仅在基类声明为虚析构函数原因:

同普通虚函数一样,它的实现也需要维护一个虚函数表,在此基础上来根据对象类型来选择使用哪个虚构函数。以上面代码为例,由于p实际指向的是B类型的对象,又由于在基类声明了虚析构函数,因此会根据类型选择调用B的析构函数,同时由于在析构派生类时也会自动调用基类的析构函数,所以最终所有的对象都会被析构。

(并不是所有c++类都应该将析构函数设置为virtual。只有具有virtual函数的多态基类(或者其它想当base class的类)才应该将析构函数设置为virtual,对于普通的类则无必要。因为虚函数的实现要求对象携带额外信息,也就是维护一个指向虚函数表的指针vptr(virtual table pointer),vptr指向虚函数表vtbl(virtual table)。当调用一个对象的虚函数时,就会通过vptr找到vtbl,在vtbl中寻找正确的函数指针调用。由于vptr的加入,导致对象大小增加。所以对于非多态基类,没必要将析构函数声明为virtual以带来额外负担。 ——引用自https://www.cnblogs.com/zhuyf87/archive/2013/03/12/2955058.html

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页