看板CompBook
标 题Re: 网际网路四大服务 答客问 (1) - reference and delete
发信站清华资讯(枫桥驿站) (Sun Mar 26 22:51:27 2000)
转信站Ptt!bbs.ee.ntu!freebsd.ntu!netnews.csie.nctu!news.cs.nthu!maple
Re: 网际网路四大服务 答客问 (1) - reference and delete
侯捷
[email protected]
2000.03.26 第一次发表於
清大.枫桥驿站(140.114.87.5).电脑书讯版(Computer/CompBook)
本文将於日後整理於 侯捷网站
侯捷网站:www.jjhou.com
----------------------------------------------------------------
网际网路四大服务 答客问 (1) 之中,我代作者王家俊先生回覆了
sammy 读者的一个问题。但我自己亦从中心生疑惑。所以我把
答客问 (1) 转给家俊。虽然家俊正在服兵役,却很快做了回覆。
以下是其来信。
jcwang wrote (2000/03/26) :
> 侯大哥,您好:
>
> 感谢您替我对读者 sammy 做了这麽详尽的回答。我想读者会选择直接
> 问侯捷而不问作者,想必是因为「侯捷」这个招牌已经成了「有问
> 必答」的代名词,写信问原作者还未必能得到如此详尽的回覆呢! :)
> 这是一件可喜可贺的事!
>
> 我把一个比较接近我原意的程式写在下方:
>
> // jcw 032600
>
> #include <iostream.h>
>
> class A // 类似原来的 CWin32TCPEnv
> {
> public:
> int val ;
> } ;
>
> class B // 类似原来的 ClientStub
> {
> public:
> B( A &initval ) : val( initval ) {}
> A &val ; // 请注意,这里是 by reference,这是关键!
> } ;
>
> int main( void )
> {
> A myA ;
> myA.val = 5 ;
>
> B *pB = new B( myA ) ; // B 将 myA 关联到它的 val 成员
>
> A &finalA = pB->val ; // 类似 CWin32TCPEnv &Env = pStub->m_Env ;
>
> cout << finalA.val << endl ; // 输出 5
>
> delete pB ;
>
> cout << finalA.val << endl ; // 仍旧输出 5。显然 delete pB
> // 并未自动解构参照成员
>
> return 0 ;
> // 这个时候 A 才被解构。替 A 写一个 destructor 便可知。
> }
>
> 所以观察到的是 desructor 并不会呼叫其「参照成员」的 destructor
> (但确定会解构其实体成员)。从我手中的资料无法判断这是 VC++ 的 bug
> 还是标准行为,不过 HelloTCPIP 能正常工作完全依据的就是这一点。
>
> 请侯大哥指教。
侯捷补充:
> 我想读者会选择直接问侯捷而不问作者,想必是因为「侯捷」这个招牌
> 已经成了「有问必答」的代名词。
不不不。侯捷绝非「有问必答」。除了对侯捷所着所译之书籍提出
疑惑或指正,我会比较积极回答之外,其他则要看时间、看心情、
看主题、看能力。这阵子我整理信件加以回覆,竟然有「上个世纪」
写来的读者来函,说实在有点不好意思。对方可能早就犯嘀咕了吧。
回到正题。上封信中,我没有仔细看书中 ClientStub's data member Env
的宣告方式,想当然尔地以 by value 方式来宣告它:
class CClientStub
{
...
CWin32TCPEnv m_Env;
};
书中其实是以 by reference 方式来宣告它:(p45, #0076)
class CClientStub
{
...
CWin32TCPEnv &m_Env; // <-- a reference member
};
换句话说 CClientStub 有一个 reference member。这便是关键所在。
我把书中程式重新简化如下:
#include <iostream>
using namespace std;
class CWin32TCPEnv
{
public:
// default ctor
CWin32TCPEnv()
: m_i1(9), m_i2(28)
{
cout << "CWin32TCPEnv default ctor \n";
}
// copy ctor
CWin32TCPEnv(const CWin32TCPEnv& Env)
: m_i1(Env.m_i1), m_i2(Env.m_i2)
{
cout << "CWin32TCPEnv copy ctor \n";
}
// dtor
~CWin32TCPEnv()
{
cout << "CWin32TCPEnv dtor \n";
}
void show() { cout << m_i1 << ' ' << m_i2 << endl; }
private:
int m_i1, m_i2;
};
class CClientStub
{
public:
// ctor
CClientStub(CWin32TCPEnv& Env)
: m_Env(Env) // (c)
{
cout << "CClientStub ctor \n";
}
// dtor
~CClientStub()
{
cout << "CClientStub dtor \n";
}
// public data member
CWin32TCPEnv &m_Env; // 注意,是个 reference member.
};
void BasicServiceThread(void* lpArg)
{
CClientStub *pStub = (CClientStub *)lpArg;
CWin32TCPEnv &Env = pStub->m_Env;
delete pStub; // CClientStub dtor (a)
// 这里测试 Env 还在否?
Env.show(); // 9 28。资料仍在,显示 delete operator 并不会解构
// 「物件内之 reference member」所代表的物件。
// scope 结束前,并未唤起 Env 的 CWin32TCPEnv dtor
// 因为此处 Env 是个 reference。 (b)
}
int main()
{
CWin32TCPEnv Env; // CWin32TCPEnv default ctor
Env.show(); // 9 28
CClientStub *pStub = new CClientStub(Env); // CClientStub ctor
BasicServiceThread(pStub);
// 这里测试 Env 还在否?
Env.show(); // 9 28
// scope 结束前,唤起 Env 的 CWin32TCPEnv dtor
}
让我们从 C++ 语言层面探讨之。reference members 和 pointer members
的表现有以下两个相同点:
(1) 当物件被解构,其 reference members 或 pointer members 所代表
(所指向)的物件并不会被一并解构。见上例 (a)
(2) pointer 或 reference 不会因为退出 scope 而使其
「所代表之物件」被自动解构。见上例 (b)。
(但可能因为其他原因而被解构)
此外,reference member 一定得於 member initialization list 中
初始化。见上例 (c)。参考《C++ Primer 中文版》p720.
针对上述 (1),Effective C++ 的 item6 给了一个忠告:
item 6 : "Use delete on pointer members in destructors."
否则会出现 memory leak 问题。
家俊说:
> 我手中的资料无法判断这是 VC++ 的 bug 还是标准行为,
> 不过 HelloTCPIP 能正常工作完全依据的就是这一点。
这是 C++ 标准行为。不过一般而言,reference 主要用於
函式的型式参数(formal parameters);一般程式较少使用
reference 独立物件。
-- the end
--
※ Origin: 枫桥驿站<bbs.cs.nthu.edu.tw> ◆ Mail: [email protected]