感知this指針 人工傳遞this指針技巧

引入

C++與C明顯的不同之處就是C++對類(class)的支持,這也是C++最初被稱作C with classes的原因。我們知道類是屬性和行爲的封裝,它既包含成員變量,也包含成員函數(或稱方法)。如果成員變量是私有的,外界則無法直接訪問,而只能求助於該類的方法,因此外界與類成員的交流基本上被限制在調用類方法上。實際上,依據C++的編程思維,是應該鼓勵這種交流方式的。在設計類的時候,應儘量將屬性修飾成private,任何時候外界想要存取屬性,都只能通過類對外開放的方法,這體現了C++的封裝性,更嚴謹、安全。

與此同時,C++也儘量與C保持兼容,它在鼓勵OOP編程思維的同時,仍然允許全局變量、全局函數存在。全局變量和全局函數,顧名思義就是在程序的任何位置均允許被訪問或調用。它們對全局可見,且全局變量的生命週期與程序的生命週期一致:在程序啓動時被創建,在程序關閉時被銷燬。這與類的成員是不同的,類成員的作用域及生命週期是比較有限的。

在C++中還有一種函數比較特別,就是靜態(static)的類方法。實際上,靜態類方法與全局函數沒有多大的差別,只不過在調用它時需顯式指定其所屬類的名稱。比如調用類Cxxx的靜態方法StaticFunc,需要寫成Cxxx::StaticFunc(...);或者用Cxxx類對象調用,如Cxxx tmp; tmp.StaticFunc(...)。

既然C++中類方法分爲普通方法和靜態方法兩種,它們有何本質區別呢?答案就在於this指針。

回想一下C++的內存區域,無論是全局函數還是類的成員函數,它們都處在代碼區,一旦加載進內存就是隻讀的。我們通過類對象調用類方法時,實際上編譯器悄悄地向類方法傳遞了一個this指針。有了this指針,纔可以方便地訪問類的成員變量。然而,靜態方法卻享受不到這種“優惠”,它不能輕易訪問到類的成員變量,因爲編譯器不會給靜態方法傳遞this指針。

接下來通過一個簡單的實驗來感知this指針的存在。

實驗

定義類CThisPointerTracer,它含有私有的成員變量int m_nPrivateMember,公開的普通類方法ShowMember以及靜態方法StaticShowMember,代碼如下所示: 

// class: CThisPointerTracer
class CThisPointerTracer
{
private:
	int m_nPrivateMember;

public:
	// constructor
	CThisPointerTracer() : m_nPrivateMember(0)
	{
		// do nothing
	}
	// method: ShowMember
	void ShowMember()
	{
		_tprintf(_T("ShowMember was called\n"));
		_tprintf(_T("The private member is %d\n"), this->m_nPrivateMember);
	}
	// staic method: StaticShowMember
	static void StaticShowMember()
	{
		_tprintf(_T("StaticShowMember was called\n"));
		this->ShowMember();
	}
};

在普通類方法ShowMember中,打印私有成員變量m_nPrivateMember,這裏故意顯式地使用this指針來引用它,目的是與靜態類方法StaticShowMember形成對比。在StaicShowMember方法中,調用this->ShowMember()是非法的,因爲靜態類方法內部並沒有this指針。

不過我們可以手工向StaticShowMember傳遞一個this指針,使得靜態類方法也能訪問私有成員變量及成員函數。修改StaticShowMember的定義爲:

// staic method: StaticShowMember
static void StaticShowMember(CThisPointerTracer* pThis)
{
	assert( pThis != NULL );
	_tprintf(_T("StaticShowMember was called\n"));
	pThis->ShowMember();
}

我們手工傳遞pThis指針後,調用pThis->ShowMember()就是合法的了。爲了防止pThis指針爲空,使用assert()函數進行檢測預防。

定義main函數爲:

int _tmain(int argc, _TCHAR* argv[])
{
	CThisPointerTracer tpt;
	CThisPointerTracer::StaticShowMember(&tpt);
	return 0;
}

運行結果如我們所期待的那樣,如下所示:

StaticShowMember was called
ShowMember was called
The private member is 0
請按任意鍵繼續. . .

小結

this指針是普通類方法與靜態類方法的本質區別。在普通類方法中可以直接使用this指針,是因爲編譯器悄悄向普通類方法傳遞了this指針。然而,在靜態類方法中不能直接使用this指針,因爲編譯器不會爲它傳遞this指針。之所以這樣做是爲了兼顧作用域與生命週期兩方面的因素:它既屬於某個特定的類,又不與任何具體的類對象綁定。

在特殊情況下,我們可以手工向靜態類方法傳遞this指針,但必須確保該指針是有效的。實際上,ATL在封裝窗口處理函數就使用了這樣的技巧。▲



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章