析构函数是否必须为虚函数?什么情况下才应该定义析构函数为虚函数?

多态是面向对象的一个基本属性,包括静态多态(编译阶段)和动态多态(运行阶段),静态多态主要是指函数参数不同产生的多态性,是在编译阶段可以识别的一种多态机制,而运行时多态则主要用于基类指针指向派生类对象时,可以通过基类指针直接调用派生类的对象函数,当然这种多态是通过虚函数实现的。

虚函数的目的就是通知系统在函数调用时能够自动识别对应的类对象类型,从而能够根据指针所指类型调用对应的类对象,实现函数调用时的多态性。对于析构函数而言,同样适用于上述规则。如果析构函数不是虚函数,那么在调用该函数时(对象被删除时)则只会调用当前对象对应的类的析构函数,这对于直接定义的对象是没有什么影响的,但是对于使用基类指向派生类的指针而言,因为基类指针实际上是基类类型,所以析构时自然只会调用基类的析构函数,这就可能产生内存泄漏(因为派生类的析构函数不被调用)。所以如果确定程序中有基类指针指向派生类的问题,则必须将基类的析构函数指定为虚函数,如此才能确保NEW出来的对象被正确的DELETE。


以下是几个示例程序,用于方便理解:


class ClxBase{
public:
ClxBase() {};
~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;};

void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};

class ClxDerived : public ClxBase{
public:
ClxDerived() {};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };

void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};


示例程序一:

int main(){ 
ClxBase base;

ClxDerived derived;
base.DoSomething();

derived.DoSomething();
return 0;
}

运行结果:

Do something in class ClxBase!

Do something in class ClxDerived!

Output from the destructor of class ClxDerived!

Output from the destructor of class ClxBase!

Output from the destructor of class ClxBase!

分析:

从以上实例程序可以发现,正常构造的对象,派生类的析构函数会主动调用基类的析构函数,所以不会存在内存泄漏问题。


示例程序二:

int main(){ 
ClxDerived *p = new ClxDerived;
p->DoSomething();
delete p;
return 0;
}

运行结果:

Do something in class ClxDerived!

Output from the destructor of class ClxDerived!

Output from the destructor of class ClxBase!

分析:

从以上结果可以发现,派生类的指针在析构时,同样是运行指针所对应类型的派生类的析构函数,而此析构函数自然会调用基类的析构函数,所以也不会产生内存泄漏。


实例程序三:

int main(){ 
ClxBase *p = new ClxDerived;
p->DoSomething();
delete p;
return 0;
}

运行结果:

Do something in class ClxBase!
Output from the destructor of class ClxBase!

分析:

从以上结果可以发现,基类指针在析构时,会调用基类的析构函数,纵然其指向派生类,但其依然会调用基类的析构,所以派生类中如有需要释放的内存空间,则必然得不到释放,从而产生内存泄漏。


示例程序四:

class ClxBase{
public:
ClxBase() {};
virtual ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;};
virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};

class ClxDerived : public ClxBase{
public:
ClxDerived() {};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};

int main(){ 
ClxBase *p = new ClxDerived;
p->DoSomething();
delete p;
return 0;
}

运行结果:

Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!

分析:

以上结果可以发现,不管是普通成员函数还是析构函数,只有定义为虚函数,才能够在基类指针指向派生类对象时调用所指向对象的响应函数,从而在函数析构时能够正确的delete掉new出来的对象,不产生内存泄漏。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值