析构函数可以自己调用
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:
1
2
3
4
5
|
s.String::~String();
// Nonvirtual call
ps->String::~String();
// Nonvirtual call
s.~String();
// Virtual call
ps->~String();
// Virtual call
|
现在有个问题,除了知道 “析构函数可以自己调用” 外, 那么什么时候必须显式调用析构函数?
先看一段现实生活中的代码吧, mfc 源码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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简化一下后:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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,就必须显式调用构造函数啊,在哪里呢?就是
1
|
new
( &pSBP->strText ) CString;
// 注意看这里
|
不过,下面的代码
1
2
3
|
CString aStr;
CString* pStr = &aStr ;
pStr->CString();
|
是编译不过的。
建议:万不得已时才使用 “placement new” 语法。只有当你真的在意对象在内存中的特定位置时才使用它。例如,你的硬件有一个内存映象的 I/O计时器设备,并且你想放置一个Clock对象在那个内存位置。
危险:你要独自承担这样的责任,传递给“placement new”操作符的指针所指向的内存区域必须足够大,并且可能需要为所创建的对象进行边界调整。编译器和运行时系统都不会进行任何的尝试来检查你做的是否正确。
简单的说吧:
为什么需要调用析构函数? 当然是为了让该对象做释放资源的善后工作, 以及在什么情况下应该调用析构函数?
想让对象释放它运行中分配的内存,但是对象本身的内存不释放(比如对象中还还有指向另一块内存的指针时的情况),或者不能用 delete 释放, 比如例子中时用 calloc 分配的内存是不能用 delete 释放的.