多态基类的析构函数应该为虚函数

多态基类的析构函数应该为虚函数


#include<iostream>
using namespace std;

class CBird
{
public:
    CBird() { cout << "CBird constructor." << endl; };
    ~CBird() { cout << "CBird destructor." << endl; };
    virtual void fly() { cout << "CBird fly." << endl; };
};

class CLark : public CBird
{
public:
    CLark() { cout << "CLark constructor." << endl; };
    ~CLark() { cout << "CLark destructor." << endl; };
    void fly() { cout << "CLark fly." << endl; }
};

int main()
{
    CBird * pBird = new CLark();
    pBird->fly();
    delete pBird;

    return 0;
}

CBird作为基类描述鸟类的一般行为和属性,因为不同鸟类的飞行特点不同,所以基类CBird将fly()声明为virtrual,希望派生类重写(overriding)该方法。CLark(lark:百灵鸟)继承自CBird,并重写了fly()。

main函数中基类CBird类型指针指向派生类CLark类型对象,并以基类指针调用fly方法,根据c++的多态特性,实际调用的是CLark的fly方法。

可以看到“pBird->fly();”的确调用了派生类CLark的fly方法。但对象析构时只调用了基类CBird的析构函数,却没有调用派生类CLark的析构函数,这种现象叫做“部分析构”。

产生这个问题的原因是:当一个派生类对象通过一个基类指针删除,并且这个基类的析构函数是非虚的,c++将不会调用整个析构函数链,结果是未定义的。所以这种情况下,只调用了基类CBird的析构函数,对象的派生部分并没有被销毁。

解决办法就是将多态基类的析构函数设置为virtual。多态基类指的是基类中至少存在一个virtual函数,具有virtual函数的类也就是想当爹(base class)的类,这样的类简称为多态基类。将CBird的析构函数设置为virtual,再看程序的输出结果。

并不是所有c++类都应该将析构函数设置为virtual。只有具有virtual函数的多态基类(或者其它想当base class的类)才应该将析构函数设置为virtual,对于普通的类则无必要。因为虚函数的实现要求对象携带额外信息,也就是维护一个指向虚函数表的指针vptr(virtual table pointer),vptr指向虚函数表vtbl(virtual table)。当调用一个对象的虚函数时,就会通过vptr找到vtbl,在vtbl中寻找正确的函数指针调用。由于vptr的加入,导致对象大小增加。所以对于非多态基类,没必要将析构函数声明为virtual以带来额外负担。这同时引出另外一条准则,如果一个类的析构函数非虚,那就说明它不想当爹,程序员要顶住诱惑,拒绝继承它,即使它“出身名门”,比如标准库中的string等等。

另外,有时还会将析构函数设置为纯虚函数(pure virtual),拥有纯虚函数的类变为抽象基类(abstract class),抽象基类不能被实例化。如果某个class只希望作为base class(不希望被实例化),但是又没有一个纯虚函数,而base class应该有一个virtual析构函数,那么此时就可以将析构函数设置为纯虚函数。

必须为纯虚析构函数提供定义,否则会出现Link错误。因为析构函数的运作方式是,最深层派生(most derived)的那个class其析构函数最先被调用。然后是其每一个base class的析构函数被调用。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 如果基类析构函数不是虚函数,它不会被其导出类的析构函数覆盖,导致在析构多态对象时不会调用导出类的析构函数。这可能会导致资源泄漏或其他错误。 举个例子,假设我们有一个基类 Shape 和它的导出类 Circle。如果 Shape 的析构函数不是虚函数,那么当我们删除一个 Circle 类型的对象时,它只会调用 Shape 的析构函数,而不是 Circle 的析构函数,因此 Circle 可能分配的任何资源都不会被正确释放。 因此,如果您有一个多态继承结构,请确保基类析构函数虚函数。 ### 回答2: 基类析构函数不是虚函数,会带来以下几个问题: 1. 对象的内存泄露:当基类指针指向派生类对象时,如果基类析构函数不是虚函数,那么当使用delete操作符删除这个基类指针时,只会调用基类析构函数,而不会调用派生类的析构函数。这样会导致派生类对象中的资源没有被正确释放,造成内存泄漏。 2. 对象切割:在基类指针指向派生类对象的情况下,如果基类析构函数不是虚函数,那么当使用基类指针删除对象时,只会调用基类析构函数,而不会调用派生类的析构函数。这样会导致对象的派生类部分没有得到正确的析构,可能导致资源泄露或行为异常。 3. 引发未定义行为:如果基类析构函数不是虚函数,且派生类有带有资源占用的成员变量时,当使用基类指针删除派生类对象时,派生类的析构函数将不会被调用。这样会导致派生类对象中的资源没有得到正确释放,可能造成程序行为的未定义。 综上所述,基类析构函数不是虚函数会导致对象的内存泄露、对象切割、以及引发未定义行为。为了避免这些问题,应该基类析构函数声明为虚函数,以确保在删除对象时能够正确调用派生类的析构函数,从而正确释放资源。 ### 回答3: 基类析构函数不是虚函数会导致多态中的对象销毁时不会调用派生类的析构函数,而只会调用基类析构函数。这可能会引发以下问题: 1. 对象不完全销毁:如果基类析构函数不是虚函数,则当我们使用基类指针指向派生类对象时,通过基类指针删除该对象时,只会调用基类析构函数,而不会调用派生类的析构函数。这将导致派生类对象的资源无法正确释放,可能会导致内存泄漏或资源泄漏。 2. 数据丢失:如果基类指针指向派生类对象,并且我们将该指针赋值给派生类对象的另一个派生类指针,然后使用该派生类指针删除对象,由于基类析构函数非虚,只会调用基类析构函数,不会调用派生类的析构函数。这将导致派生类对象的成员数据无法正确释放,可能会导致数据丢失或程序行为异常。 3. 无法正确释放资源:在多态中,我们通常通过基类指针来管理派生类对象,当需要释放基类指针所指向的对象时,如果基类析构函数不是虚函数,只会调用基类析构函数,而不会调用派生类的析构函数。这将导致派生类对象中的资源无法正确释放,可能会导致资源泄漏或导致程序异常。 因此,为了避免上述问题,通常在基类中的析构函数应该声明为虚函数,以确保派生类对象在销毁时能够正确调用派生类的析构函数,从而保证资源的正确释放和程序的正常运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值