1. 内存泄漏实例
今天检查代码时发现一个因为使用抽象类引起的内存泄漏。相关代码简化如下:
//抽象类
class AbstratcClass {
//省略其他代码
virtual void do_something() = 0;
}
//实现类
class ImpClass: public AbstractClass {
//省略其他代码
virtual void do_something();
}
//应用类
class AppClass {
//省略其他代码
void work() {
AbstractClass* p = new ImpClass();
//省略其他代码
delete p; // 此处即使调用了 delete 删除抽象类指针,也无法释放实现类的内存,造成内存泄漏。
}
}
2. 利用虚析构函数,释放实现类资源
采用虚析构函数,抽象类和实现类都定义虚析构函数,代码如下:
//抽象类
class AbstratcClass {
//省略其他代码
virtual ~AbstractClass(){}
virtual void do_something() = 0;
}
//实现类
class ImpClass: public AbstractClass {
//省略其他代码
virtual virtual ~ImpClass(){}
virtual void do_something();
}
//应用类
class AppClass {
//省略其他代码
void work() {
AbstractClass* p = new ImpClass();
//省略其他代码
delete p; //因为定义了虚析构函数,所以实际上会调用 ImpClass 的析构函数。
}
}
3. 定义接口函数,显式释放资源
增加 release() 函数,显式释放实现类资源。修改后代码如下,解决内存泄漏问题:
//抽象类
class IMyInterface {
//省略其他代码
virtual void release() = 0;
virtual void do_something() = 0;
}
//实现类
class ImpClass: public IMyInterface {
//省略其他代码
virtual void release() {
delete this; //用接口函数 release 间接调用实现类的析构函数,释放资源。
}
virtual void do_something();
}
//应用类
class AppClass {
//省略其他代码
void work() {
IMyInterface * intf = new ImpClass();
//省略其他代码
intf->release(); //通过接口函数释放资源和析构。
}
}
这种方式在用抽象类实现纯粹的接口定义时,比较合适。我个人比较喜欢最后这个解决方案,这个方案抽象类作为一个纯粹的接口存在,自身仅包含接口函数声明,不包含任何可执行的代码。