C++中類成員的訪問控制權限
引入三種訪問控制符
C++中,存在三種訪問控制修飾符,它們分別是:
- public // 公有成員
- protected // 保護成員
- 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, 類可以訪問自身的所有成員 }; 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成員 }
從以上實踐中可以得出以下結論:
- 類可以訪問自身的所有成員,不論是private, protected 還是 public。
- 物件只能訪問類的public成員。
友元的訪問許可權
在以上例子的基礎上,讓我們來考慮一下,對於該類中被不同訪問控制修飾符修飾的成員,該類的友元函式和友元類對這些成員都有什麼樣的訪問許可權。
#include <iostream> using namespace std; class CBase; class CFriend; void ClearBaseA(CBase &obj); class CBase { friendCFriend;// 宣告CFriend為自己的友元類 friendvoid 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成員,當然更是可以訪問的了,雖然以上例子中並沒有驗證這一點。
所以,我們可以得出以下結論:
- 友元函式或友元類可以訪問類中的所有成員。
小結
我們換一個角度,通過以下表格總結一下。
訪問控制修飾符 | 類 | 物件 | 友元 |
---|---|---|---|
public | 可訪問 | 可訪問 | 可訪問 |
protected | 可訪問 | 不可訪問 | 可訪問 |
private | 可訪問 | 不可訪問 | 可訪問 |
引入三種繼承方式
在C++中,在繼承的過程中,有以下三種繼承方式,它們分別是:
- public (公有繼承)
- protected (保護繼承)
-
private (私有繼承)
這三個關鍵字與之前的三種訪問控制修飾符剛好相同,但在這裡,它們有不同的意義。
-
對於public繼承,基類中的成員的訪問控制修飾符不作任何改動,原樣繼承到派生類中。
也就是說,基類中的public成員,到了派生類中,仍然是派生類的public成員;基類中的protected成員,到了派生類中,仍然是protected成員;基類中的private成員,它對派生類不可見。 - 對於protected繼承,基類中的public成員,在派生類中被派生為protected成員;基類中的protected成員,在派生類中仍然是protected成員;基類中的private成員,在派生類不可見。
- 對於private繼承,基類中的public和protected成員,在派生類中,均被派生為了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成員) }
由以上例子可以看出:
- 基類中的private, protected, public成員,經由public繼承之後,在派生類中分別為不可見private, protected,public成員。
- 派生類中不能訪問基類的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成員) }
由以上例子可以看出:
- 基類中的private, protected, public成員,經由protected繼承之後,在派生類中分別為不可見private, protected,protected成員。
- 派生類中不能訪問基類的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; }
由以上例子可以看出:
- 基類中的private, protected, public成員,經由private繼承之後,在派生類中均不可見。
- 派生類中不能訪問基類的private成員,但可以訪問基類的private和protected成員。
小結
- 不論何種繼承方式,派生類都不能訪問基類的private成員,它只能訪問基類的public和protected成員。
- 三種繼承方式對不同訪問控制符修飾的成員的影響如下表所示。
訪問控制修飾符 | public繼承 | protected繼承 | private繼承 |
---|---|---|---|
public | public | protected | private |
protected | protected | protected | private |
private | private | private | private |
總結
- 友元和類一樣,可以訪問類的所有成員。
- 物件只能訪問類的public成員。
- 派生類只能訪問基類的public和protected成員,而不能訪問基類的private成員。
- 對於派生出來的類,首先根據繼承方式,確定基類各成員在經指定的繼承方式繼承後的訪問控制權限(經繼承後基類各成員是變成了public,protected還是private),然後根據第1、2、3點對各成員進行訪問。
- 經繼承後,基類中的成員會根據繼承方式,對各成員的訪問控制符進行修改。修改之後,基類中的private成員對派生類不可見。
參考文獻
- 鄭莉 C++語言程式設計ofollow,noindex">類的友元 繼承方式簡介及公有繼承 私有繼承和保護繼承
-
菜鳥教程C++ 類訪問修飾符