Kontrola dostępu do elementów członkowskich (C++)

Kontrolki dostępu umożliwiają oddzielenie public interfejsu klasy od private szczegółów implementacji i protected składowych, które są używane tylko przez klasy pochodne. Specyfikator dostępu ma zastosowanie do wszystkich elementów członkowskich zadeklarowanych po nim do momentu następnego wystąpienia specyfikatora dostępu.

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();
};

Domyślny dostęp znajduje się private w klasie i public w ramach struktury lub unii. Specyfikatory dostępu w klasie mogą być używane dowolną liczbę razy w dowolnej kolejności. Alokacja magazynu dla obiektów typów klas jest zależna od implementacji. Jednak kompilatory muszą zagwarantować przypisanie elementów członkowskich do kolejnych wyższych adresów pamięci między specyfikatorami dostępu.

Kontrola dostępu do elementów członkowskich

Typ dostępu Znaczenie
private Składowe klasy zadeklarowane jako private mogą być używane tylko przez funkcje składowe i znajomi (klasy lub funkcje) klasy.
protected Składowe klasy zadeklarowane jako protected mogą być używane przez funkcje składowe i znajomi (klasy lub funkcje) klasy. Ponadto mogą być używane przez klasy pochodzące z klasy .
public Składowe klasy zadeklarowane jako public mogą być używane przez dowolną funkcję.

Kontrola dostępu pomaga zapobiegać używaniu obiektów w sposób, w jaki nie były one przeznaczone do użycia. Ta ochrona zostanie utracona podczas jawnej konwersji typów (rzutów).

Uwaga

Kontrola dostępu ma równie zastosowanie do wszystkich nazw: funkcji składowych, danych składowych, klas zagnieżdżonych i modułów wyliczania.

Kontrola dostępu w klasach pochodnych

Dwa czynniki kontrolują, które składowe klasy bazowej są dostępne w klasie pochodnej; te same czynniki kontrolują dostęp do dziedziczych składowych w klasie pochodnej:

  • Czy klasa pochodna deklaruje klasę bazową przy użyciu specyfikatora public dostępu.

  • Jaki jest dostęp do składowej w klasie bazowej.

W poniższej tabeli przedstawiono interakcję między tymi czynnikami a sposobem określania dostępu do składowych klasy bazowej.

Dostęp członkowski w klasie bazowej

private protected public
Zawsze niedostępny z dowolnym dostępem pochodnym private w klasie pochodnej, jeśli używasz private wyprowadzania private w klasie pochodnej, jeśli używasz private wyprowadzania
protected w klasie pochodnej, jeśli używasz protected wyprowadzania protected w klasie pochodnej, jeśli używasz protected wyprowadzania
protected w klasie pochodnej, jeśli używasz public wyprowadzania public w klasie pochodnej, jeśli używasz public wyprowadzania

W poniższym przykładzie pokazano wyprowadzanie dostępu:

// 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
}

W DerivedClass1systemie funkcja PublicFunc składowa jest składową public i ProtectedFunc jest składową, ponieważ BaseClass jest klasą protected bazowąpublic. PrivateFunc parametr ma private wartość BaseClassi jest niedostępny dla wszystkich klas pochodnych.

W DerivedClass2systemie funkcje PublicFunc i ProtectedFunc są traktowane jako private elementy członkowskie, ponieważ BaseClass jest klasą bazową private . Ponownie jest to privateBaseClass, PrivateFunc i jest niedostępny dla wszystkich klas pochodnych.

Klasę pochodną można zadeklarować bez specyfikatora dostępu klasy bazowej. W takim przypadku wyprowadzanie jest brane pod uwagę private , jeśli deklaracja klasy pochodnej używa słowa kluczowego class . Wyprowadzanie jest brane pod uwagę public , jeśli deklaracja klasy pochodnej używa słowa kluczowego struct . Na przykład następujący kod:

class Derived : Base
...

jest odpowiednikiem:

class Derived : private Base
...

Podobnie następujący kod:

struct Derived : Base
...

jest odpowiednikiem:

struct Derived : public Base
...

Elementy członkowskie zadeklarowane jako mające private dostęp nie są dostępne dla funkcji ani klas pochodnych, chyba że te funkcje lub klasy są deklarowane przy użyciu friend deklaracji w klasie bazowej.

Typ union nie może mieć klasy bazowej.

Uwaga

Podczas określania klasy bazowej private zaleca się jawne użycie private słowa kluczowego, aby użytkownicy klasy pochodnej rozumieli dostęp do składowych.

Kontrola dostępu i statyczne elementy członkowskie

Gdy określisz klasę bazową jako private, będzie ona mieć wpływ tylko na niestatyczne elementy członkowskie. Publiczne statyczne elementy członkowskie są nadal dostępne w klasach pochodnych. Jednak uzyskiwanie dostępu do składowych klasy bazowej przy użyciu wskaźników, odwołań lub obiektów może wymagać konwersji, która ponownie stosuje kontrolę dostępu. Rozważmy następujący przykład:

// 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;
}

W poprzednim kodzie kontrola dostępu uniemożliwia konwersję ze wskaźnika na wskaźnik do Derived2 wskaźnika do wskaźnika do Base. Wskaźnik this jest niejawnie typu Derived2 *. Aby wybrać CountOf funkcję, this należy przekonwertować na typ Base *. Taka konwersja nie jest dozwolona, ponieważ Base jest to pośrednia private klasa bazowa do Derived2klasy . Konwersja na typ klasy bazowej private jest akceptowalna tylko dla wskaźników do natychmiastowych klas pochodnych. Dlatego wskaźniki typu Derived1 * można przekonwertować na typ Base *.

Jawne wywołanie CountOf funkcji bez użycia wskaźnika, odwołania lub obiektu do jej wybrania nie oznacza konwersji. Dlatego połączenie jest dozwolone.

Elementy członkowskie i przyjaciele klasy Tpochodnej , mogą przekonwertować wskaźnik na wskaźnik na T bezpośrednią klasę Tbazową private klasy .

Dostęp do funkcji wirtualnych

Kontrola dostępu zastosowana do virtual funkcji jest określana przez typ używany do wywołania funkcji. Przesłanianie deklaracji funkcji nie wpływa na kontrolę dostępu dla danego typu. Przykład:

// 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;
}

W poprzednim przykładzie wywołanie funkcji GetState wirtualnej przy użyciu wskaźnika do wpisywania VFuncBase wywołań VFuncDerived::GetStatejest GetState traktowane jako public. Jednak wywoływanie GetState przy użyciu wskaźnika do typu VFuncDerived jest naruszeniem kontroli dostępu, ponieważ GetState jest zadeklarowane private w klasie VFuncDerived.

Uwaga

Funkcję GetState wirtualną można wywołać za pomocą wskaźnika do klasy VFuncBasebazowej . Nie oznacza to, że wywoływana funkcja jest wersją klasy bazowej tej funkcji.

Kontrola dostępu z wieloma dziedziczeniami

W przypadku kłótni z wieloma dziedziczeniami obejmujących wirtualne klasy bazowe można uzyskać dostęp do podanej nazwy za pośrednictwem więcej niż jednej ścieżki. Ze względu na to, że można zastosować różne ścieżki kontroli dostępu, kompilator wybiera ścieżkę, która zapewnia największy dostęp. Zobacz następującą ilustrację:

Diagram showing access along the paths of an inheritance graph.

Na diagramie przedstawiono następującą hierarchię dziedziczenia: klasa VBase jest klasą bazową. Klasa LeftPath dziedziczy z bazy danych VBase przy użyciu wirtualnej bazy danych private VBase. klasa RightPath dziedziczy również z bazy danych VBase, ale przy użyciu wirtualnej bazy danych public VBase. Na koniec klasa Pochodna dziedziczy zarówno z klasy LeftPath, jak i z klasy RightPath przy użyciu leftPath public , public RightPath.

Dostęp do ścieżek grafu dziedziczenia

Na rysunku nazwa zadeklarowana w klasie VBase jest zawsze osiągana za pośrednictwem klasy RightPath. Właściwa ścieżka jest bardziej dostępna, ponieważ RightPath deklaruje VBase jako klasę bazową public , a jednocześnie LeftPath deklaruje VBase jako private.

Zobacz też

Dokumentacja języka C++