1,036 views
首页 > 技术心得 > 什么时候必须显式调用析构函数

什么时候必须显式调用析构函数

2010年3月3日

析构函数可以自己调用

msdn says:
Calling a destructor explicitly is seldom necessary. However, it can be useful to perform cleanup of objects placed at absolute addresses. These objects are commonly allocated using a user-defined new operator that takes a placement argument. The delete operator cannot deallocate this memory because it is not allocated from the free store . A call to the destructor, however, can perform appropriate cleanup. To explicitly call the destructor for an object, s, of class String, use one of the following statements:

s.String::~String();     // Nonvirtual call
ps->String::~String();   // Nonvirtual call

s.~String();       // Virtual call
ps->~String();     // Virtual call

现在有个问题,除了知道 “析构函数可以自己调用” 外, 那么什么时候必须显式调用析构函数?

先看一段现实生活中的代码吧, mfc 源码:

BOOL CStatusBar::AllocElements(int nElements, int cbElement)
{
	int i;

	// destruct old elements
	AFX_STATUSPANE* pSBP = _GetPanePtr(0);
	for (i = 0; i < m_nCount; i++)
	{
		pSBP->strText.~CString(); // 注意看这里
		++pSBP;
	}

	// allocate new elements
	if (!CControlBar::AllocElements(nElements, cbElement))
		return FALSE;

	// construct new elements
	pSBP = _GetPanePtr(0);
	for (i = 0; i < m_nCount; i++)
	{
#pragma push_macro("new")
#undef new
		new( &pSBP->strText ) CString; // 注意看这里
#pragma pop_macro("new")
		++pSBP;
	}
	return TRUE;
}

在上面的代码中,就有显式调用 CString 的析构函数的代码。cool。
因为还调用了CControlBar::AllocElements(),上面的代码不是很明显,我把CControlBar::AllocElements简化一下后:

BOOL CControlBar::AllocElements(int nElements, int cbElement)
{
	ASSERT_VALID(this);
	ENSURE_ARG(nElements >= 0 && cbElement >= 0);
	ENSURE(m_pData != NULL || m_nCount == 0);

	// allocate new data if necessary
	void* pData = NULL;
	if (nElements > 0)
	{
		ENSURE_ARG(cbElement > 0);
		if ((pData = calloc(nElements, cbElement)) == NULL)  // 注意这里 : calloc
			return FALSE;
	}
	free(m_pData);      // free old data  // 注意这里 : free

	// set new data and elements
	m_pData = pData;
	m_nCount = nElements;

	return TRUE;
}

这个时候,如果注意到我特别注释的 free 函数调用,可能已经意识到了为什么要显式调用析构函数了。
如果还没有,那么可以问自己一个面试常规问题:delete和free有什么区别?答:delete会使析构函数被调用。
或者反过来说,free 没有调用析构函数,那么怎么办?所以你必须自己显式调用析构函数。

上面的这个例子可以这样抽象下,现在需要 free 掉一块内存,而那块内存中,还有一个类,类里面还有指针,(这里是CString)需要在析构函数中释放内存。因为用的是free,所以那个类的析构函数不会自动被调用,这个时候,就必须显式调用那个类的析构函数。

另外继续问个面试问题,new和calloc的区别?哈,构造的函数的调用啊
所以,上面的代码用的calloc,就必须显式调用构造函数啊,在哪里呢?就是

new( &pSBP->strText ) CString; // 注意看这里

不过,下面的代码

CString aStr;
CString* pStr = &aStr ;
pStr->CString();

是编译不过的。

建议:万不得已时才使用 “placement new” 语法。只有当你真的在意对象在内存中的特定位置时才使用它。例如,你的硬件有一个内存映象的 I/O计时器设备,并且你想放置一个Clock对象在那个内存位置。

危险:你要独自承担这样的责任,传递给“placement new”操作符的指针所指向的内存区域必须足够大,并且可能需要为所创建的对象进行边界调整。编译器和运行时系统都不会进行任何的尝试来检查你做的是否正确。

简单的说吧:
为什么需要调用析构函数? 当然是为了让该对象做释放资源的善后工作, 以及在什么情况下应该调用析构函数?
想让对象释放它运行中分配的内存,但是对象本身的内存不释放(比如对象中还还有指向另一块内存的指针时的情况),或者不能用 delete 释放, 比如例子中时用 calloc 分配的内存是不能用 delete 释放的.

技术心得

  1. 目前还没有任何评论.
  1. 目前还没有任何 trackbacks 和 pingbacks.