1. 程式人生 > >C++中類成員的訪問控制權限

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成員
}

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

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

友元的訪問許可權

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

#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成員;基類中的protected成員,到了派生類中,仍然是protected成員;基類中的private成員,它對派生類不可見。
  2. 對於protected繼承,基類中的public成員,在派生類中被派生為protected成員;基類中的protected成員,在派生類中仍然是protected成員;基類中的private成員,在派生類不可見。
  3. 對於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成員)
}

由以上例子可以看出:

  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成員對派生類不可見。

參考文獻