C++訪問控制符詳解

一、前言

    C++標準中定義了三種類成員訪問控制符:public、protected和private,分別譯爲公有的、保護的、私有的。當然,這是在一個類的內部聲明這三種控制符的意思,那要是在一個繼承體系中會是怎麼樣的呢?


二、控制符訪問特點

    首先說明這三種訪問控制符各自的訪問控制特點,指出這三種控制符聲明的變量可以被哪些實體訪問:

    (1)public成員:能被本類的成員函數(不管是哪一種控制符聲明的成員函數,都可以)、友元函數、本類的對象、其派生類的成員函數(不管是哪一種控制符聲明的成員函數,都可以);

    (2)protected成員:能被本類的成員函數(不管是哪一種控制符聲明的成員函數,都可以)、友元函數、其派生類的成員函數(不管是哪一種控制符聲明的成員函數,都可以);

    (3)private成員:能被本類的成員函數(不管是哪一種控制符聲明的成員函數,都可以)、友元函數;

通過以上介紹,可以總結爲:

    (1)對類的用戶而言,即使用類對象實例的人(還有一種說法就是指只能看到類的頭文件的人,因爲設計類的人都將他寫的實現文件編譯成了二進制文件,在Windows下是.lib、.obj後綴的文件),通過類對象實例,只能訪問到類的public成員(函數或變量);

    (2)對類本身而言,即類的設計者,在設計類的過程中,他可以通過類的所有的成員函數(三種控制符聲明),或者友元函數訪問其類內部所有的成員(函數或變量),不管其要該成員(函數或變量)是用哪一種控制符聲明的,都可以訪問。

    (3)對該類的派生類而言,到這裏只能說基類的public、protected的成員對它來說是可見的,即可以訪問,但具體怎麼訪問,這涉及到了C++類繼承的知識,接下來會重點解釋這一條。


三、C++訪問控制與繼承

    在一個繼承體系中,會涉及到兩層訪問控制符,一層在類的派生列表中,另一層則是在基類中。龐大而複雜的繼承體系看起來是不好理解的(其實也確實不好理解,哈哈),但它歸根結底是單層繼承的擴展,所以這裏我就以單層public繼承結構來說明C++訪問控制與繼承的關係,繼承結構爲Drived  :public Base。

    既然這裏涉及到繼承,那先必須要理解的一點就是C++繼承體系中派生類的對象模型,這在C++Primer和《深度探索C++對象模型》都有解釋,即派生類的內存模型主要包括兩部分:基類部分和派生類部分,其中基類部分是從基類繼承而來的,派生類部分是自己定義的。

    上述第一層訪問控制符(即派生列表中)影響的是派生類中基類部分成員(函數或變量)的訪問控制權限(也僅僅只有這一點影響),具體的影響爲:

        (1)public繼承(保持不變):派生類中基類部分的成員(函數或變量)的訪問控制權限和其在基類中聲明的權限一樣;

        (2)protected繼承(各自降低一個權限):基類中public成員在派生類中變成protected權限,其他成員都變爲private權限;

        (3)private繼承(全部私有化):派生類中基類部分的成員(函數或變量)的訪問控制權限全部變爲private成員。

好,現在解釋了第一層訪問控制符,那麼派生類中所有成員(基類部分由上面的可以解釋,派生類部分本身就已經聲明)的訪問控制權限都確定了,那現在的問題就是能不能訪問了。

    針對這裏的單層public繼承結構的例子,基類部分的成員的訪問控制權限保持不變,下面分三種情況進行解釋:

        (1)基類部分的public成員。請注意,儘管派生類的內存模型分爲兩部分,但這兩部分都屬於派生類對象。所以派生類(類本身)的所有成員函數(不管哪一種訪問控制符)都訪問基類部分的public成員(函數或變量),同時派生類對象也直接可以訪問(通過成員訪問操作符)。

        (2)基類部分的protected成員。派生類(類本身)的所有成員函數(不管哪一種訪問控制符)都訪問基類部分的protected成員(函數或變量),但派生類對象不能直接訪問(基類對象自己都不能訪問它的protected成員,派生類當然不可以)。

        (3)基類部分的private成員。派生類(類本身)的所有成員函數(不管哪一種訪問控制符)都不能訪問基類部分的private成員(函數或變量),同樣派生類對象也不能直接訪問

下面對以上三種情況在VS2010中進行測試,這裏只進行編輯,VS2010自動檢錯功能就能提示相應錯誤

class Base
{
public:
	int pub_mem;
protected:
	int pro_mem;
private:
	int pri_mem;

//對類自身來說,其成員函數,無論何種權限說明,都可以訪問類中的任何權限的成員變量
public:
	int base_fun_pub() {}
protected:
	int base_fun_pro() {}
private:
	int base_fun_pri() {}
};

class Drived: public Base
{
//pub、pro、pri成員函數都能訪問其基類部分的public、protected成員變量,但都不能訪問private成員
public:
	int drived_fun_pub()
	{
		pub_mem = 0;     //情況(1)
		pro_mem = 0;     //情況(2)
		pri_mem = 0;     //情況(3)(錯誤,不能訪問)
	}
protected:
	int drived_fun_pro()
	{
		pub_mem = 0;    //情況(1)
		pro_mem = 0;    //情況(2)
		pri_mem = 0;    //情況(3)(錯誤,不能訪問)
	}
private:
	int drived_fun_pri()
	{
		pub_mem = 0;   //情況(1)
		pro_mem = 0;   //情況(2)
		pri_mem = 0;    //情況(3)(錯誤,不能訪問)
	}
private:
	int j;
};

void main()
{
	/******************單層public繼承爲例**********************/
	Drived drivedobj;
	drivedobj.pub_mem;    //情況(1)
	drivedobj.pro_mem;    //情況(2)(錯誤,不能訪問)
	drivedobj.pri_mem;    //情況(3)(錯誤,不能訪問)
}
在VS2010中顯示的結果是(自動下劃紅線提示的就是訪問權限錯誤):


四、總結

    其實不管繼承還是不繼承,都可以當做是在一個單層類(沒有繼承)中進行訪問控制權限分析,因爲在繼承情況下無非是多了一個繼承列表中的訪問控制符,這個控制符影響的是基類中的成員在派生類中是什麼樣的訪問權限(只有這一點影響),而這點影響非常好解釋(見上面第三節解釋),一旦解釋了派生列表中的訪問控制符,就可以視爲一個單一類的成員來訪問,所以,解釋一個類的成員訪問權限的步驟爲:

    1、如果是派生類,記住兩點

        (1)先解釋派生列表中的控制符,確定基類中各成員在派生類中是什麼屬性;

        (2)對派生類而言,派生類對象只能訪問基類中原有屬性爲public的成員,派生類本身(成員函數)只能訪問基類中原有屬性爲protected和public的成員(注意,是原有屬性,不管經過派生列表訪問控制符解釋之後的屬性是什麼)。

    2、如果不是派生類

        (1)其對象只能訪問public屬性的成員;

        (2)對於類的接口實現代碼(所有三種屬性的成員函數)來說,它能訪問所有訪問權限的成員。

    所以說,對於繼承體系來說,無疑就多出了一層疑惑:即基類中成員到了派生類中是什麼訪問屬性的?一旦解決了這個問題,那就變成了單一類的訪問問題了。


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