Re: 網際網路四大服務 答客問 (1) - reference and …
Re: 網際網路四大服務 答客問 (1) - reference and delete
侯捷 jjhou@ccca.nctu.edu.tw
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: jjhou@ccca.nctu.edu.tw
討論串 (同標題文章)
CompBook 近期熱門文章
PTT數位生活區 即時熱門文章