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

析构函数可以自己调用

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 释放的.

地址: http://blog.tinybrowser.net/archives/1507#more-1507
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值