C++中類成員的訪問控制


結論

首先給出結論,請看下圖,看圖說話最容易理解了。

類眼中的自己

類中定義的所有成員,不論是以public, protected還是private修飾,對類自身而言,它們都是可見的。

對象眼中的類

站在類的對象的角度去觀察類,這時,只有類中的public成員是可見的。而其中的protected和private成員對對象而言,是不可見的。

友元眼中的類

站在友元的角度,類中所有的成員,不論是以public, protected還是private修飾,對友元而言,它們都是可見的。

派生類眼中的基類

派生類只能看見基類中的public和protected成員。而基類中的private成員,對於派生類而言,是不可見的。有一點必須注意,這里的public, protected和private均是指基類在被繼承之后所呈現出來的成員訪問權限。
下表展示了不同的繼承方式,對基類中各成員訪問權限的影響。

基類中所用訪問控制修飾符 public繼承 protected繼承 private繼承
public public protected private
protected protected protected private
private private private private

所以,在派生類中,對於基類中的各成員的訪問權限,我們可以按如下步驟去判斷:

  1. 根據上表,確定基類中各成員在被繼承之后訪問權限的變化。
  2. 根據“ 派生類只能看見基類中的public和protected成員” 這一點來確定哪些基類成員可以被訪問。

好了,到這里為止,結論就全部說完了。
接下來全是對結論的驗證,所以,無需從頭至尾瀏覽,請有針對性的選擇查看。

引入三種訪問控制符

C++中,存在三種訪問控制修飾符,它們分別是:

  • public // 公有成員
  • protected // 保護成員
  • private // 私有成員

術語

為了使文章容易理解,我們對以下術語作出說明:

  • 對象: 與類相對,對象是類的實例。
  • 派生類:與基類相對,派生類就是子類。
  • 繼承:繼承與派生是一個意思。繼承偏重指出此過程中不變的部分,而派生的意思則更偏向於在原有基礎上所新增加的部分。
  • 成員:類中成員變量和成員函數的統稱。

實踐1--對象的訪問權限

在以下的例子中,我們創建了一個簡單的類。
下面,我們就來探究一下,對於該類中被不同訪問控制修飾符修飾的成員,它們對於該類的對象都有什么樣的訪問權限。

#include <iostream>

using namespace std;

class CBase
{
private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}

	int getA() const {return a_base_private;}		// OK, 類可以訪問自身的所有成員
	int getB() const {return b_base_protected;}		// OK, 類可以訪問自身的所有成員
	int getC() const {return c_base_public;}		// OK, 類可以訪問自身的所有成員
};

int main()
{
	int tmp;
	CBase baseObj;
	
	//baseObj.a_base_private = 1;			// KO, 對象不能訪問類的private成員
	//baseObj.b_base_protected = 1;			// KO, 對象不能訪問類的protected成員
	baseObj.c_base_public = 1;				// OK, 對象可以訪問類的public成員
	
	tmp = baseObj.getA();		// OK, 對象可以訪問類的public成員
	tmp = baseObj.getB();		// OK, 對象可以訪問類的public成員
	tmp = baseObj.getC();		// OK, 對象可以訪問類的public成員
}

從以上實踐中可以得出以下結論:

  1. 類可以訪問自身的所有成員,不論是private, protected 還是 public。
  2. 對象只能訪問類的public成員。

實踐2--友元的訪問權限

在以上例子的基礎上,讓我們來考慮一下,對於該類中被不同訪問控制修飾符修飾的成員,該類的友元函數和友元類對這些成員都有什么樣的訪問權限。

#include <iostream>

using namespace std;

class CBase;
class CFriend;
void ClearBaseA(CBase &obj);

class CBase
{
friend 	CFriend;							// 聲明CFriend為自己的友元類
friend 	void ClearBaseB(CBase &obj);		// 聲明ClearBaseA為自己的友元函數

private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}

	int getA() const {return a_base_private;}		// OK, 類可以訪問自身的所有成員
	int getB() const {return b_base_protected;}		// OK, 類可以訪問自身的所有成員
	int getC() const {return c_base_public;}		// OK, 類可以訪問自身的所有成員
};

class CFriend
{
private:
	CBase obj;
	
public:
	CFriend(){}
	~CFriend(){}
	
	int setBaseA(int f) {obj.a_base_private = f;}		// OK, 在友元類中,可以訪問Base類的私有成員
	int getBaseA() const {return obj.getA();}
};

void ClearBaseB(CBase &obj)
{
	obj.b_base_protected = 0;			// OK, 在友元函數中,可以訪問Base類的保護成員
}

int main()
{
	int tmp;
	CBase baseObj;
	CFriend friendObj;
	
	cout << baseObj.getB() << endl;		// 通過構造函數初始化為2
	ClearBaseB(baseObj);
	cout << baseObj.getB() << endl;		// 被友元函數給清0了
	
	cout << friendObj.getBaseA() << endl;	// 通過構造函數初始化為1
	friendObj.setBaseA(7);
	cout << friendObj.getBaseA() << endl;	// 被友元類給設置為了7
}

由上例中可以看出,友元可以訪問類中的private和protected成員,對於public成員,當然更是可以訪問的了,雖然以上例子中並沒有驗證這一點。
所以,我們可以得出以下結論:

  1. 友元函數或友元類可以訪問類中的所有成員。
    另外,關於友元還有一點需要注意。在友元中,只能通過對象來訪問聲明友元的類成員。

小結

現在,讓我們換一個角度,通過以下表格總結一下。

訪問控制修飾符 對象 友元
public 可見 可見 可見
protected 可見 不可見 可見
private 可見 不可見 可見

引入三種繼承方式

在C++中,在繼承的過程中,有以下三種繼承方式,它們分別是:

  • public (公有繼承)
  • protected (保護繼承)
  • private (私有繼承)
    這三個關鍵字與之前的三種訪問控制修飾符剛好相同,但在這里,它們有不同的意義。
  1. 對於public繼承,基類中的成員的訪問控制修飾符不作任何改動,原樣繼承到派生類中。
    也就是說,基類中的public成員,在被以public方式繼承之后,仍然是基類的public成員;基類中的protected成員,仍然是protected成員;基類中的private成員,它仍然是private成員。注意,在繼承之后,基類中的private成員對於派生類是不可見的。
  2. 對於protected繼承,基類中的public成員,在被以protected方式繼承之后,它變成了基類的protected成員;基類中的protected成員,則仍然是protected成員;基類中的private成員,則仍然是private成員。注意,在繼承之后,基類中的private成員對於派生類是不可見的。
  3. 對於private繼承,基類中的public和protected和private成員,在被以private方式繼承之后,在基類中均成為了private成員;而基類中的private成員,對派生類不可見。

public繼承方式

在第一個例子的基礎之上,我們通過public方式繼承出一個新的派生類。

#include <iostream>

using namespace std;

class CBase
{
private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}

	int getA() const {return a_base_private;}		// OK, 類可以訪問自身的所有成員
	int getB() const {return b_base_protected;}		// OK, 類可以訪問自身的所有成員
	int getC() const {return c_base_public;}		// OK, 類可以訪問自身的所有成員
};

class CDerived:public CBase
{
private:
	int x_derived_private;
protected:
	int y_derived_protected;
public:
	int z_derived_private;
	
public:
	CDerived(){x_derived_private = 4; y_derived_protected = 5; z_derived_private = 6;}
	~CDerived(){}
	
	//void setBaseA(int t){a_base_private = t;}		// KO, 派生類中不能訪問基類的private成員
	void setBaseB(int t){b_base_protected = t;}		// OK, 派生類中可以訪問基類的protected成員
	void setBaseC(int t){c_base_public = t;}		// OK, 派生類中可以訪問基類的public成員	

	int getX() const {return x_derived_private;}
	int getY() const {return y_derived_protected;}
	int getZ() const {return z_derived_private;}
};

int main()
{
	CDerived derivedObj;
	
	//derivedObj.a_base_private = 1;			// KO, 基類中由private修飾的a_base_private,對派生類是不可見的,即使在派生類中都不能訪問,更別提派生類對象了。 
	//derivedObj.b_base_protected = 1;			// KO, 對象不能訪問類的protected成員(public方式繼承的protected成員,在派生類中仍為protected成員)
	derivedObj.c_base_public = 1;				// OK, 對象可以訪問類的public成員(public方式繼承的public成員,在派生類中仍為public成員)
	
	cout << derivedObj.getA() << endl;		// OK, 對象可以訪問類的public成員(public方式繼承的public成員,在派生類中仍為public成員)
	derivedObj.setBaseB(8);					// OK, 對象可以訪問類的public成員
	cout << derivedObj.getB() << endl;		// OK, 對象可以訪問類的public成員(public方式繼承的public成員,在派生類中仍為public成員)
	derivedObj.setBaseC(9);					// OK, 對象可以訪問類的public成員
	cout << derivedObj.getC() << endl;		// OK, 對象可以訪問類的public成員(public方式繼承的public成員,在派生類中仍為public成員)
}

由以上例子可以看出:

  1. 基類中的private, protected, public成員,經由public繼承之后,在派生類中分別為不可見private, protected,public成員。
  2. 派生類中不能訪問基類的private成員,但可以訪問基類的private和protected成員。

protected繼承方式

在第一個例子的基礎之上,我們通過protected方式繼承出一個新的派生類。

#include <iostream>

using namespace std;

class CBase
{
private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}

	int getA() const {return a_base_private;}		// OK, 類可以訪問自身的所有成員
	int getB() const {return b_base_protected;}		// OK, 類可以訪問自身的所有成員
	int getC() const {return c_base_public;}		// OK, 類可以訪問自身的所有成員
};

class CDerived:protected CBase
{
private:
	int x_derived_private;
protected:
	int y_derived_protected;
public:
	int z_derived_private;
	
public:
	CDerived(){x_derived_private = 4; y_derived_protected = 5; z_derived_private = 6;}
	~CDerived(){}

	//void setBaseA(int t){a_base_private = t;}		// KO, 派生類中不能訪問基類的private成員
	void setBaseB(int t){b_base_protected = t;}		// OK, 派生類中可以訪問基類的protected成員
	void setBaseC(int t){c_base_public = t;}		// OK, 派生類中可以訪問基類的public成員

	int getX() const {return x_derived_private;}	// OK, 類可以訪問自身的所有成員
	int getY() const {return y_derived_protected;}	// OK, 類可以訪問自身的所有成員
	int getZ() const {return z_derived_private;}	// OK, 類可以訪問自身的所有成員
};

int main()
{
	CDerived derivedObj;
	
	//derivedObj.a_base_private = 1;			// KO, 對象不能訪問類的private成員(protected方式繼承的private成員,在派生類中不可見)
	//derivedObj.b_base_protected = 1;			// KO, 對象不能訪問類的protected成員(protected方式繼承的protected成員,在派生類中仍為protected成員)
	//derivedObj.c_base_public = 1;				// KO, 對象不可以訪問類的protected成員(protected方式繼承的public成員,在派生類中成為protected成員)
	
	//cout << derivedObj.getA() << endl;		// KO, 對象不可以訪問類的protected成員(protected方式繼承的public成員,在派生類中成為protected成員)
	//cout << derivedObj.getB() << endl;		// KO, 對象不可以訪問類的protected成員(protected方式繼承的public成員,在派生類中成為protected成員)
	//cout << derivedObj.getC() << endl;		// KO, 對象不可以訪問類的protected成員(protected方式繼承的public成員,在派生類中成為protected成員)
}

由以上例子可以看出:

  1. 基類中的private, protected, public成員,經由protected繼承之后,在派生類中分別為不可見private, protected,protected成員。
  2. 派生類中不能訪問基類的private成員,但可以訪問基類的private和protected成員。

private繼承方式

在第一個例子的基礎之上,我們通過private方式繼承出一個新的派生類。

#include <iostream>

using namespace std;

class CBase
{
private:
	int a_base_private;
protected:
	int b_base_protected;
public:
	int c_base_public;
	
public:	
	CBase(){a_base_private = 1; b_base_protected = 2; c_base_public = 3;}
	~CBase(){}

	int getA() const {return a_base_private;}		// OK, 類可以訪問自身的所有成員
	int getB() const {return b_base_protected;}		// OK, 類可以訪問自身的所有成員
	int getC() const {return c_base_public;}		// OK, 類可以訪問自身的所有成員
};

class CDerived:private CBase
{
private:
	int x_derived_private;
protected:
	int y_derived_protected;
public:
	int z_derived_private;
	
public:
	CDerived(){x_derived_private = 4; y_derived_protected = 5; z_derived_private = 6;}
	~CDerived(){}

	//void setBaseA(int t){a_base_private = t;}		// KO, 派生類中不能訪問基類的private成員,因為其在派生類中不可見
	void setBaseB(int t){b_base_protected = t;}		// OK, 派生類中可以訪問基類的protected成員
	void setBaseC(int t){c_base_public = t;}		// OK, 派生類中可以訪問基類的public成員

	int getX() const {return x_derived_private;}	// OK, 類可以訪問自身的所有成員
	int getY() const {return y_derived_protected;}	// OK, 類可以訪問自身的所有成員
	int getZ() const {return z_derived_private;}	// OK, 類可以訪問自身的所有成員
};

int main()
{
	CDerived derivedObj;
	
	//derivedObj.a_base_private = 1;			// KO, (private方式繼承的private成員,在派生類中不可見)
	//derivedObj.b_base_protected = 1;			// KO, (private方式繼承的protected成員,在派生類中不可見)
	//derivedObj.c_base_public = 1;				// KO, (private方式繼承的public成員,在派生類中成為不可見)
	
	//cout << derivedObj.getA() << endl;		// KO, (private方式繼承的public成員,在派生類中不可見)
	//cout << derivedObj.getB() << endl;		// KO, (private方式繼承的public成員,在派生類中不可見)
	//cout << derivedObj.getC() << endl;		// KO, (private方式繼承的public成員,在派生類中不可見)
	
	cout << derivedObj.getX() << endl;
	cout << derivedObj.getY() << endl;
	cout << derivedObj.getZ() << endl;
}

由以上例子可以看出:

  1. 基類中的private, protected, public成員,經由private繼承之后,在派生類中均不可見。
  2. 派生類中不能訪問基類的private成員,但可以訪問基類的private和protected成員。

小結

  • 不論何種繼承方式,派生類都不能訪問基類的private成員,它只能訪問基類的public和protected成員。
  • 三種繼承方式對不同訪問控制符修飾的成員的影響如下表所示。
基類中所用訪問控制修飾符 public繼承 protected繼承 private繼承
public public protected private
protected protected protected private
private private private private

總結

  1. 友元和類一樣,可以訪問類的所有成員。
  2. 對象只能訪問類的public成員。
  3. 派生類只能訪問基類的public和protected成員,而不能訪問基類的private成員。
  4. 對於派生出來的類,首先根據繼承方式,確定基類各成員在經指定的繼承方式繼承后的訪問控制權限(經繼承后基類各成員是變成了public,protected還是private),然后根據第1、2、3點對各成員進行訪問。
  5. 經繼承后,基類中的成員會根據繼承方式,對各成員的訪問控制符進行修改。修改之后,基類中的private成員對派生類不可見。

參考文獻


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM