成員存取控制 (C++)

存取控制可讓您將類別的介面與 private 實作詳細資料和 protected 僅供衍生類別使用的成員區隔 public 開。 除非發現下一個存取規範,否則存取規範會套用至在其後宣告的所有成員。

class Point
{
public:
    Point( int, int ) // Declare public constructor.;
    Point();// Declare public default constructor.
    int &x( int ); // Declare public accessor.
    int &y( int ); // Declare public accessor.

private:                 // Declare private state variables.
    int _x;
    int _y;

protected:      // Declare protected function for derived classes only.
    Point ToWindowCoords();
};

預設存取權位於 private 類別中,以及 public 結構或等位。 您可以依任意順序使用類別中的存取規範任何次數。 類別類型物件的儲存體配置與實作相關。 不過,編譯器必須保證將成員指派給存取規範之間連續較高的記憶體位址。

成員存取控制

存取的類型 意義
private 宣告為 的 private 類別成員只能由 類別的成員函式和 friend(類別或函式)使用。
protected 宣告為 的 protected 類別成員可由 類別的成員函式和 friend(類別或函式)使用。 此外,類別所衍生的類別也可以使用這些類別成員。
public 宣告為 的 public 類別成員可供任何函式使用。

存取控制可協助防止您使用物件的方式使用物件。 當您進行明確的類型轉換時,就會遺失此保護。

注意

存取控制同樣適用於所有名稱:成員函式、成員資料、巢狀類別及列舉程式。

衍生類別中的存取控制

在衍生類別中可存取哪些基底類別的成員是由兩個因素所控制,這些相同的因素可控制在衍生類別中對於繼承成員的存取:

  • 衍生類別是否使用 public 存取規範宣告基類。

  • 要存取哪些基底類別的成員。

下表顯示這些因素之間的互動,以及如何判斷基底類別成員存取。

基底類別中的成員存取

private protected public
任何衍生存取一律無法存取 private 如果您使用衍生,則為 private 衍生類別中的 private 如果您使用衍生,則為 private 衍生類別中的
protected 如果您使用衍生,則為 protected 衍生類別中的 protected 如果您使用衍生,則為 protected 衍生類別中的
protected 如果您使用衍生,則為 public 衍生類別中的 public 如果您使用衍生,則為 public 衍生類別中的

下列範例說明存取衍生:

// access_specifiers_for_base_classes.cpp
class BaseClass
{
public:
    int PublicFunc(); // Declare a public member.
protected:
    int ProtectedFunc(); // Declare a protected member.
private:
    int PrivateFunc(); // Declare a private member.
};

// Declare two classes derived from BaseClass.
class DerivedClass1 : public BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

class DerivedClass2 : private BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

int main()
{
    DerivedClass1 derived_class1;
    DerivedClass2 derived_class2;
    derived_class1.PublicFunc();
    derived_class2.PublicFunc(); // function is inaccessible
}

在 中 DerivedClass1 ,成員函式 PublicFunc 是成員 public ,而且 ProtectedFunc 是成員, protected 因為 BaseClasspublic 基類。 PrivateFuncprivateBaseClass 而且無法存取任何衍生類別。

在 中 DerivedClass2 ,函式 PublicFuncProtectedFunc 會被視為 private 成員,因為 BaseClassprivate 基類。 同樣地, PrivateFuncprivateBaseClass 而且無法存取任何衍生類別。

您可以在未使用基底類別存取指定名稱的情況下宣告衍生類別。 在這種情況下,如果衍生類別宣告使用 class 關鍵字,則會考慮 private 衍生。 如果衍生類別宣告使用 struct 關鍵字,則會考慮 public 衍生。 例如,下列程式碼:

class Derived : Base
...

等於:

class Derived : private Base
...

同樣地,下列程式碼:

struct Derived : Base
...

等於:

struct Derived : public Base
...

宣告為具有 private 存取權的成員無法存取函式或衍生類別,除非這些函式或類別是使用 friend 基類中的宣告來宣告。

類型 union 不能有基類。

注意

指定 private 基類時,建議明確使用 private 關鍵字,讓衍生類別的使用者瞭解成員存取權。

存取控制和靜態成員

當您將基類指定為 private 時,它只會影響非靜態成員。 在衍生類別中仍然可以存取公用的靜態成員。 不過,使用指標、參考或物件來存取基類的成員,可能需要轉換,以再次套用存取控制。 請考慮下列範例:

// access_control.cpp
class Base
{
public:
    int Print();             // Nonstatic member.
    static int CountOf();    // Static member.
};

// Derived1 declares Base as a private base class.
class Derived1 : private Base
{
};

// Derived2 declares Derived1 as a public base class.
class Derived2 : public Derived1
{
    int ShowCount();    // Nonstatic member.
};

// Define ShowCount function for Derived2.
int Derived2::ShowCount()
{
    // Call static member function CountOf explicitly.
    int cCount = ::Base::CountOf();     // OK.

    // Call static member function CountOf using pointer.
    cCount = this->CountOf();  // C2247: 'Base::CountOf'
                               // not accessible because
                               // 'Derived1' uses 'private'
                               // to inherit from 'Base'
    return cCount;
}

在上述程式碼中,存取控制項禁止從 Derived2 的指標轉換為 Base 的指標。 指標 this 隱含的類型 Derived2 * 為 。 若要選取函 CountOf 式, this 必須轉換成 類型 Base * 。 不允許這類轉換,因為 Base 是 private 的 Derived2 間接基類。 只有直接衍生類別的指標可以接受轉換成 private 基類類型。 這就是為什麼型 Derived1 * 別的指標可以轉換成 型別 Base *

未使用指標、參考或物件來選取函式的 CountOf 明確呼叫,表示沒有轉換。 這就是允許呼叫的原因。

衍生類別 T 的成員和朋友可以將指標 T 轉換成 的直接基類 T 指標 private 。

存取虛擬函式

套用至 virtual 函式的存取控制是由用來進行函式呼叫的類型所決定。 覆寫函式的宣告不會影響指定類型的存取控制。 例如:

// access_to_virtual_functions.cpp
class VFuncBase
{
public:
    virtual int GetState() { return _state; }
protected:
    int _state;
};

class VFuncDerived : public VFuncBase
{
private:
    int GetState() { return _state; }
};

int main()
{
   VFuncDerived vfd;             // Object of derived type.
   VFuncBase *pvfb = &vfd;       // Pointer to base type.
   VFuncDerived *pvfd = &vfd;    // Pointer to derived type.
   int State;

   State = pvfb->GetState();     // GetState is public.
   State = pvfd->GetState();     // C2248 error expected; GetState is private;
}

在上述範例中,使用類型 VFuncBase 呼叫 VFuncDerived::GetState 的指標呼叫虛擬函 GetState 式,並將 GetState 視為 public 。 不過,使用 型別指標呼叫 GetState 是存取控制違規,因為 GetState 是在 類別 VFuncDerived 中宣告 privateVFuncDerived

警告

虛擬函式 GetState 可以使用基底類別 VFuncBase的指標呼叫。 這並不表示所呼叫的函式是該函式的基類版本。

具有多重繼承的存取控制

在包含虛擬基底類別的多重繼承斜格紋中,可以透過多個路徑存取指定的名稱。 由於可以依循這些不同路徑套用不同的存取控制,編譯器會選擇授予較多存取權的路徑。 請參閱下圖:

Diagram showing access along the paths of an inheritance graph.

下圖顯示下列繼承階層:類別 VBase 是基類。 Class LeftPath 會使用虛擬 private VBase 繼承自 VBase。 類別 RightPath 也會繼承自 VBase,但使用虛擬 public VBase。 最後,衍生類別繼承自使用 public LeftPath、 public RightPath 的 LeftPath 類別和 Class RightPath。

依序繼承圖形的路徑進行存取

在圖中,類別 VBase 中宣告的名稱一定會透過類別 RightPath 進行存取。 正確的路徑更容易存取,因為 RightPath 宣告 VBasepublic 基類,同時 LeftPath 宣告 VBaseprivate

另請參閱

C++ 語言參考