先看一段能够正常执行的代码,但会造成内存泄漏:
deleteObject.h
实现删除一个CObjectItem的派生类的指针所指的内存
#pragma once namespace smtlCheck { class CObjectItem; class CDeleteObject { public: CDeleteObject(void); ~CDeleteObject(void); void deleteObject(CObjectItem *vObj); }; }
deleteObject.cpp
#include "deleteObject.h" using namespace smtlCheck; CDeleteObject::CDeleteObject(void) { } CDeleteObject::~CDeleteObject(void) { } void CDeleteObject::deleteObject(CObjectItem *vObj) { <span style="color:#ff0000;">delete vObj;</span> }
object.h
基类
#pragma once #include <iostream> namespace smtlCheck { class CObjectItem { public: CObjectItem() {} virtual ~CObjectItem() { //print(); } private: virtual void print() const = 0;//just for testing whether the deconstruction is called }; }
DivObject.h
派生类
#pragma once #include <iostream> #include <string> #include <vector> #include "object.h" namespace smtlCheck { class CDivObject : public CObjectItem { public: CDivObject() { m_pSig = new std::string(""); m_pIntSet = new std::vector<int>[10]; } virtual ~CDivObject() { delete m_pSig; delete [] m_pIntSet; <span style="color:#ff0000;">print();</span> } //other members... private: std::string *m_pSig; std::vector<int> *m_pIntSet; virtual void print() const { std::cout << "the deconstruction of CDivObject is called" << std::endl; } }; }
main.cpp
#include "deleteObject.h" #include "DivObject.h" int main() { CObjectItem *pDidObj = new CDivObject; //do something... CDeleteObject DeObj; <span style="color:#ff0000;">DeObj.deleteObject(pDidObj);</span> return 0; }
运行程序会发现CDivObject的析构函数并没有调用。这时什么原因呢?
主要原因是在deleteObject.cpp这个文件里面,class CObjectItem这个类定义不完整,所以下面的代码:
void CDeleteObject::deleteObject(CObjectItem *vObj) { <span style="color:#ff0000;">delete vObj;</span> }
之中delete删除一个不完整定义类的指针,解决方法:
在deleteObject.cpp中加上COjectItem的头文件。但这不是一个通用的解决方法,因为这种错误很难被发现,至少是编译通过(C++中,delete一个类型不完整的类对象的指针,编译器会发出警告,不幸的是,程序员有时候会忽略这种警告),最坑人的是居然能够运行,这样的错误查找起来实在是很困难。
有没有在编译器就能够找出delete一个不完整定义的指针呢?boost就提供了一个这种功能:
boost::checkde_delete可以在编译时期发现这种错误。
修改代码如下:
void CDeleteObject::deleteObject(CObjectItem *vObj) { boost::checked_delete(vObj); }
加上头文件boost/checked_delete.hpp
这样我们在编译时会发现如下错误:
error C2027: use of undefined type ‘smtlCheck::CObjectItem‘
告诉我们CObjectItem类没有定义,在deleteObject.cpp的头文件中加上object.h,这样就解决问题了。
checked_delete的实现
template<class T> inline void checked_delete(T * x) { // intentionally complex - simplification causes regressions typedef char type_must_be_complete[ <span style="color:#ff0000;">sizeof(T)</span>? 1: -1 ]; (void) sizeof(type_must_be_complete); delete x; }
主要利用sizeof(T),sizeof在编译时期要知道T的大小,如果T不是一个完整的定义就会在编译是报错。
这样就可以避免delete一个不完整类型的指针。
sizeof(T)返回0的情况在有些编译器上会出现:如gcc int a[0];sizeof(a)就返回0 vs上则报错。