Różnica między spadkiem prywatnym, publicznym i chronionym

Odpowiedzi:

1064

Aby odpowiedzieć na to pytanie, chciałbym najpierw opisać osoby przystępujące do członkostwa własnymi słowami. Jeśli już to wiesz, przejdź do nagłówka „dalej:”.

Istnieją trzy Akcesory że jestem świadomy: public, protectedi private.

Pozwolić:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • Wszystko, co jest świadome, Basejest również świadome, co Basezawiera publicMember.
  • Tylko dzieci (i ich dzieci) są świadomi, że Basezawiera protectedMember.
  • Nikt nie Basejest tego świadomy privateMember.

Przez „jest świadomy” mam na myśli „potwierdzić istnienie, a zatem mieć dostęp”.

Kolejny:

To samo dzieje się z dziedzictwem publicznym, prywatnym i chronionym. Rozważmy klasę Basei klasę, Childktóra dziedziczy Base.

  • Jeśli dziedziczenie jest public, wszystko, co jest świadome Basei Childświadome, że Childdziedziczy Base.
  • Jeśli dziedzictwo jest protectedtylko Childi jego dzieci, są świadome, że dziedziczą Base.
  • Jeśli dziedzictwo jest private, nikt inny nie Childjest świadomy dziedzictwa.
Anzurio
źródło
182
Chciałbym dodać kilka słów, że widoczność w C ++ opiera się na klasie zamiast na obiekcie, co oznacza, że ​​obiekty tej samej klasy mogą uzyskać dostęp do swoich prywatnych pól bez ograniczeń.
Zhe Chen
48
Jeśli trudno ci to zrozumieć, przeczytaj odpowiedź Kirilla V. Lyadvinsky'ego, a następnie wróć i przeczytaj to.
The Vivandiere
6
To tylko kolejny przypadek, który ilustruje, w jaki sposób dziedziczenie po SomeBasejest po prostu sztywnym sposobem na skomponowanie anonimowego członka typu SomeBase. To, jak każdy inny członek, ma specyfikator dostępu, który ma taką samą kontrolę nad dostępem zewnętrznym.
underscore_d
1
@ZheChen, jeśli mam obiekty Tom i Jerry z klasy Osoba z wiekiem prywatnym, w jaki sposób uzyskujesz (i modyfikujesz?) Wiek Jerry'ego za pomocą Toma?
gen
2
Czy potrafisz zilustrować, co rozumiesz przez „świadomy„ dziedzictwa ”? Rozumiem „mogę uzyskać dostęp do tego, nie mogę uzyskać dostępu do tego”, ale nie rozumiem tego, gdy ktoś mówi: „Wiem, że A dziedziczy po B”. Co ja tu robię, czy sprawdzam dziedzictwo?
neilxdims,
1458
class A 
{
public:
    int x;
protected:
    int y;
private:
    int z;
};

class B : public A
{
    // x is public
    // y is protected
    // z is not accessible from B
};

class C : protected A
{
    // x is protected
    // y is protected
    // z is not accessible from C
};

class D : private A    // 'private' is default for classes
{
    // x is private
    // y is private
    // z is not accessible from D
};

WAŻNA UWAGA: wszystkie klasy B, C i D zawierają zmienne x, y i z. To tylko kwestia dostępu.

O korzystaniu z chronionego i prywatnego dziedzictwa można przeczytać tutaj .

Kirill V. Lyadvinsky
źródło
35
To, co napisał Anzurio, kliknęło tylko w połączeniu z twoją odpowiedzią bezpośrednio poniżej. Плус 1.
Nie będę istniał Idonotexist
2
Moje zrozumienie tego, jak to działało, było TAK DALEKO! Dziękuję bardzo za wyjaśnienie.
tjwrona1992
zrozumienie tego zajęło mi trochę czasu. Ale teraz jest jasne. Dzięki!
Chan Kim
115

Ograniczenie widoczności dziedziczenia spowoduje, że kod nie będzie mógł zobaczyć, że niektóre klasy dziedziczą inną klasę: niejawna konwersja z pochodnej do bazy nie będzie działać, a static_castz bazy do pochodnej nie będzie działać.

Tylko członkowie / przyjaciele klasy mogą widzieć prywatne dziedzictwo, a tylko członkowie / przyjaciele i klasy pochodne mogą widzieć chronione dziedzictwo.

dziedzictwo publiczne

  1. Dziedziczenie IS-A. Przycisk jest oknem i wszędzie tam, gdzie potrzebne jest okno, można również przekazać przycisk.

    class button : public window { };

chronione dziedzictwo

  1. Chronione zaimplementowane zgodnie z warunkami. Rzadko przydatne. Używany w boost::compressed_paircelu czerpania z pustych klas i oszczędzania pamięci przy użyciu pustej optymalizacji klas podstawowych (przykład poniżej nie używa szablonu, aby być na bieżąco):

    struct empty_pair_impl : protected empty_class_1 
    { non_empty_class_2 second; };
    
    struct pair : private empty_pair_impl {
      non_empty_class_2 &second() {
        return this->second;
      }
    
      empty_class_1 &first() {
        return *this; // notice we return *this!
      }
    };

spadek prywatny

  1. Wdrożono w kategoriach. Zastosowanie klasy podstawowej służy wyłącznie do implementacji klasy pochodnej. Przydatne w przypadku cech i jeśli rozmiar ma znaczenie (puste cechy, które zawierają tylko funkcje, wykorzystają optymalizację pustej klasy podstawowej). Często jednak lepszym rozwiązaniem jest powstrzymywanie . Rozmiar ciągów ma krytyczne znaczenie, dlatego jest to często spotykane zastosowanie

    template<typename StorageModel>
    struct string : private StorageModel {
    public:
      void realloc() {
        // uses inherited function
        StorageModel::realloc();
      }
    };

członek publiczny

  1. Agregat

    class pair {
    public:
      First first;
      Second second;
    };
  2. Akcesoria

    class window {
    public:
        int getWidth() const;
    };

członek chroniony

  1. Zapewnienie rozszerzonego dostępu dla klas pochodnych

    class stack {
    protected:
      vector<element> c;
    };
    
    class window {
    protected:
      void registerClass(window_descriptor w);
    };

członek prywatny

  1. Zachowaj szczegóły implementacji

    class window {
    private:
      int width;
    };

Zauważ, że rzutowanie w stylu C celowo pozwala rzutować klasę pochodną na chronioną lub prywatną klasę bazową w określony i bezpieczny sposób, a także rzucić w innym kierunku. Należy tego unikać za wszelką cenę, ponieważ może on uzależnić kod od szczegółów implementacji - ale w razie potrzeby można skorzystać z tej techniki.

Johannes Schaub - litb
źródło
7
Myślę, że Scott Myers (tak jak lubię jego rzeczy) ma wiele do powiedzenia na ogólne zamieszanie. Myślę, że jego analogie do IS-A i IS-IMPLEMENTED-IN-TERMS-OF są wystarczające do tego, co się dzieje.
DangerMouse
65

Te trzy słowa kluczowe są również używane w zupełnie innym kontekście, aby określić model dziedziczenia widoczności .

W tej tabeli zebrano wszystkie możliwe kombinacje modelu deklaracji i modelu dziedziczenia przedstawiające wynikowy dostęp do komponentów, gdy podklasa jest całkowicie zdefiniowana.

wprowadź opis zdjęcia tutaj

Powyższa tabela jest interpretowana w następujący sposób (spójrz na pierwszy wiersz):

jeśli komponent jest zadeklarowany jako publiczny, a jego klasa jest dziedziczona jako publiczna, wynikowy dostęp jest publiczny .

Przykład:

 class Super {
    public:      int p;
    private:     int q;
    protected:   int r;
 };

 class Sub : private Super {};

 class Subsub : public Sub {};

Uzyskany dostęp do zmiennych p, q, rw klasie Subsub jest żaden .

Inny przykład:

class Super {
    private:     int x;
    protected:   int y;
    public:      int z;
 };
class Sub : protected Super {};

Uzyskany dostęp do zmiennych y, zw klasie Sub jest chroniony i dla zmiennej xjest żaden .

Bardziej szczegółowy przykład:

class Super {
private:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};
int main(void) {
    Super object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

Teraz pozwala zdefiniować podklasę:

class Sub : Super { };

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

Zdefiniowana klasa o nazwie Sub, która jest podklasą klasy o nazwie Superlub ta Subklasa jest pochodną Superklasy. Do Subklasy wprowadza ani nowych zmiennych ani nowe funkcje. Czy to oznacza, że ​​jakikolwiek obiekt Subklasy dziedziczy wszystkie cechy po tym, jak Superklasa jest w rzeczywistości kopią obiektów Superklasy?

Nie . Tak nie jest.

Jeśli skompilujemy następujący kod, otrzymamy tylko błędy kompilacji, mówiąc, że puti getmetody są niedostępne. Dlaczego?

Gdy pominiemy specyfikator widoczności, kompilator zakłada, że ​​zastosujemy tak zwane dziedziczenie prywatne . Oznacza to, że wszystkie publiczne składniki nadklasy zamieniają się w prywatny dostęp, prywatne składniki nadklasy w ogóle nie będą dostępne. Oznacza to w związku z tym, że nie wolno używać tego ostatniego w podklasie.

Musimy poinformować kompilator, że chcemy zachować wcześniej używaną politykę dostępu.

class Sub : public Super { };

Nie daj się wprowadzić w błąd : nie oznacza to, że prywatne elementy klasy Super (takie jak zmienna pamięci) zamieniają się w publiczne w nieco magiczny sposób. Prywatne komponenty pozostaną prywatne , publiczne pozostaną publiczne .

Obiekty Subklasy mogą robić „prawie” te same rzeczy, co ich starsze rodzeństwo utworzone z Superklasy. „Prawie”, ponieważ fakt bycia podklasą oznacza również, że klasa utraciła dostęp do prywatnych elementów nadklasy . Nie możemy napisać funkcji Subskładowej klasy, która byłaby w stanie bezpośrednio manipulować zmienną pamięci.

To bardzo poważne ograniczenie. Czy istnieje jakieś obejście?

Tak .

Trzeci poziom dostępu nazywa się chroniony . Słowo kluczowe chronione oznacza, że ​​oznaczony nim komponent zachowuje się jak publiczny, gdy jest używany przez jedną z podklas i wygląda jak prywatny dla reszty świata . - Dotyczy to tylko klas dziedziczonych publicznie (takich jak klasa Super w naszym przykładzie) -

class Super {
protected:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};

class Sub : public Super {
public:
    void print(void) {cout << "storage = " << storage;}
};

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get() + 1);
    object.print();
    return 0;
}

Jak widać w przykładowym kodzie, mamy nową funkcjonalność dla Subklasy i robi ona jedną ważną rzecz: uzyskuje dostęp do zmiennej pamięci z klasy Super .

Nie byłoby to możliwe, gdyby zmienna została zadeklarowana jako prywatna. W głównym zakresie funkcji zmienna i tak pozostaje ukryta, więc jeśli napiszesz coś takiego:

object.storage = 0;

Kompilator poinformuje Cię, że jest to plik error: 'int Super::storage' is protected.

Wreszcie ostatni program wygeneruje następujące dane wyjściowe:

storage = 101
BugShotGG
źródło
4
Pierwszy, który wspomina o braku modyfikatora (jak w klasie: SuperClass) daje wartość prywatną. Jest to ważny element, którego brakuje innym, wraz z dokładnymi wyjaśnieniami. +1
woda,
2
Przesadzaj IMO, ale na początku podoba mi się ten stół.
cp.engr
63

Ma to związek z tym, jak publiczni członkowie klasy podstawowej są narażeni na działanie klasy pochodnej.

  • public -> publiczni członkowie klasy podstawowej będą publiczni (zwykle domyślny)
  • chronione -> członkowie publiczni klasy podstawowej będą chronieni
  • private -> członkowie publiczni klasy podstawowej będą prywatni

Jak wskazuje litb, dziedziczenie publiczne jest tradycyjnym dziedzictwem, które można zobaczyć w większości języków programowania. To znaczy modeluje relację „IS-A”. Dziedziczenie prywatne, coś, co AFAIK jest specyficzne dla C ++, to relacja „WDROŻONE W WARUNKACH”. Oznacza to, że chcesz korzystać z interfejsu publicznego w klasie pochodnej, ale nie chcesz, aby użytkownik klasy pochodnej miał dostęp do tego interfejsu. Wielu argumentuje, że w tym przypadku należy agregować klasę podstawową, czyli zamiast mieć klasę bazową jako bazę prywatną, w elemencie pochodnym w celu ponownego wykorzystania funkcjonalności klasy bazowej.

Doug T.
źródło
13
Lepiej powiedz „publicznie: dziedzictwo będzie widoczne dla wszystkich”. chronione: dziedzictwo będzie widoczne tylko dla klas pochodnych i przyjaciół ”,„ prywatne: dziedzictwo będzie widoczne tylko dla samej klasy i przyjaciół ”. To różni się od twojego sformułowania, ponieważ nie tylko członkowie mogą być niewidoczni, ale także relacja IS-A może być niewidoczna
Johannes Schaub - litb
4
Kiedyś skorzystałem z dziedziczenia prywatnego, aby zrobić dokładnie to, co opisuje Doug T, tj. „Chcesz użyć interfejsu publicznego w klasie pochodnej, ale nie chcesz, aby użytkownik klasy pochodnej miał dostęp do tego interfejsu”. Zasadniczo użyłem go do uszczelnienia starego interfejsu i ujawnienia innego poprzez klasę pochodną.
Bogaty
36
Member in base class : Private   Protected   Public   

Typ dziedziczenia :              Obiekt odziedziczony jako :

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public
kinshuk4
źródło
23
To wprowadza w błąd. Prywatni członkowie klasy podstawowej zachowują się zupełnie inaczej niż zwykli prywatni członkowie klasy - w ogóle nie są dostępni z klasy pochodnej. Myślę, że kolumna z trzema „Prywatnymi” powinna być kolumną „Niedostępne”. Zobacz odpowiedź Kirilla V. Lyadvinsky'ego na to pytanie.
Sam Kauffman,
27

1) Dziedziczenie publiczne :

za. Prywatni członkowie klasy podstawowej nie są dostępni w klasie pochodnej.

b. Chronieni członkowie klasy podstawowej pozostają chronieni w klasie pochodnej.

do. Członkowie publiczni klasy podstawowej pozostają publiczni w klasie pochodnej.

Tak więc inne klasy mogą korzystać z publicznych elementów klasy Base za pośrednictwem obiektu klasy Derived.

2) Dziedziczenie chronione :

za. Prywatni członkowie klasy podstawowej nie są dostępni w klasie pochodnej.

b. Chronieni członkowie klasy podstawowej pozostają chronieni w klasie pochodnej.

do. Publiczni członkowie klasy Base również stają się chronionymi członkami klasy Derived.

Tak więc inne klasy nie mogą używać publicznych członków klasy Base za pośrednictwem obiektu klasy Derived; ale są dostępne dla podklasy pochodnej.

3) Dziedziczenie prywatne :

za. Prywatni członkowie klasy podstawowej nie są dostępni w klasie pochodnej.

b. Chronieni i publiczni członkowie klasy Base stają się prywatnymi członkami klasy Derived.

Zatem do innych klas klasy Base nie można uzyskać dostępu przez inne klasy za pośrednictwem obiektu klasy Derived, ponieważ są one prywatne w klasie Derived. Zatem nawet podklasa klasy pochodnej nie może uzyskać do nich dostępu.

yuvi
źródło
20

Dziedziczenie publiczne modeluje związek IS-A. Z

class B {};
class D : public B {};

każdy D jest B .

Dziedziczenie prywatne modeluje relację IMPLEMENTOWANĄ-UŻYTKOWĄ (lub jakkolwiek to się nazywa). Z

class B {};
class D : private B {};

a nieD jestB , ale każdy Dużywa jej Bw jego realizacji. Prywatne dziedziczenie można zawsze wyeliminować, używając zamiast tego zabezpieczenia:

class B {};
class D {
  private: 
    B b_;
};

To Drównież można zrealizować za pomocą B, w tym przypadku za pomocą jego b_. Ograniczenie jest mniej ścisłym sprzężeniem między typami niż dziedziczenie, więc ogólnie powinno być preferowane. Czasami stosowanie ograniczania zamiast dziedziczenia prywatnego nie jest tak wygodne, jak dziedziczenie prywatne. Często jest to kiepska wymówka dla lenistwa.

Nie sądzę, żeby ktokolwiek wiedział, jakie protectedmodele dziedziczenia. Przynajmniej nie widziałem jeszcze żadnego przekonującego wyjaśnienia.

sbi
źródło
Niektórzy mówią, że jako związek. Jak używanie krzesła jako młota. Oto krzesło: chroniony młot
4951
kiedy stosowanie ograniczania zamiast dziedziczenia prywatnego nie jest tak wygodne jak dziedziczenie prywatne? Czy możesz to wyjaśnić na przykładzie?
Destructor,
@Pravasi: Jeśli Dwywodzi się prywatnie D, może zastąpić funkcje wirtualne B. (Jeśli, na przykład, Bjest interfejsem obserwatora, wówczas Dmógłby go zaimplementować i przekazać thisdo funkcji wymagających takiego interfejsu, bez możliwości korzystania z niego przez wszystkich Djako obserwatora.) DMoże również selektywnie udostępnić członków Bswojego interfejsu poprzez działanie using B::member. Oba są niewygodne pod względem składniowym, gdy Bsą członkami.
sbi
@sbi: stare, ale ... powstrzymywanie jest nieuniknione w przypadku CRTP i / lub wirtualnych (jak poprawnie opisałeś w komentarzu - ale oznacza to, że nie można go modelować jako powstrzymywanie, jeśli B ma metody abstrakcyjne nie wolno go dotykać). protecteddziedziczenie, które uznałem za przydatne w przypadku virtualklasy podstawowej i protectedctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
lorro,
11

Jeśli dziedziczysz publicznie po innej klasie, wszyscy wiedzą, że dziedziczysz i każdy może używać polimorficznie przez wskaźnik klasy bazowej.

Jeśli odziedziczysz w sposób chroniony, tylko twoje dzieci będą mogły korzystać z ciebie polimorficznie.

Jeśli odziedziczysz prywatnie, tylko ty będziesz mógł wykonywać metody klas nadrzędnych.

Co w gruncie rzeczy symbolizuje wiedzę reszty klas na temat twojego związku z klasą nadrzędną

Arkaitz Jimenez
źródło
9

Dostęp do chronionych danych mogą uzyskać wszystkie klasy, które dziedziczą po twojej klasie. Prywatni członkowie danych nie mogą jednak. Powiedzmy, że mamy następujące elementy:

class MyClass {
    private:
        int myPrivateMember;    // lol
    protected:
        int myProtectedMember;
};

Od wewnątrz rozszerzenia do tej klasy odwołania this.myPrivateMembernie będą działać. Jednak this.myProtectedMemberbędzie. Wartość ta jest wciąż enkapsulowana, więc jeśli mamy instancję tej klasy o nazwie myObj, to myObj.myProtectedMembernie będzie działać, więc działa podobnie do prywatnego członka danych.

Andrew Noyes
źródło
8
Accessors    | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public       |      y     |       y       |   y
—————————————+————————————+———————————————+———————
protected    |      y     |       y       |   n
—————————————+————————————+———————————————+———————
private      |            |               |    
  or         |      y     |       n       |   n
no accessor  |            |               |

y: accessible
n: not accessible

Na podstawie tego przykładu dla java ... Myślę, że stolik wart tysiąca słów :)

Enissay
źródło
Java ma tylko dziedzictwo publiczne
Zelldon
To nie jest temat mówienia o Javie, ale NIE, mylisz się ... Kliknij link w mojej odpowiedzi powyżej, aby uzyskać szczegółowe informacje
Enissay
Wspomniałeś o Javie, więc to jest temat. Twój przykład obsługuje specyfikatory używane w jaca. Pytanie dotyczy specyfikatorów dziedziczenia, które nie istnieją w Javie i zrobiły różnicę. Jeśli pole w nadklasie jest publiczne, a dziedziczenie jest prywatne, pole jest dostępne tylko w podklasie. Na zewnątrz nie ma wskazania, czy podklasa rozszerza nadklasę. Ale twoja tabela wyjaśnia tylko specyfikatory dla pola i metod.
Zelldon
7

Podsumowanie:

  • Prywatne: nikt nie może tego zobaczyć, z wyjątkiem klasy
  • Chronione: Prywatne + klasy pochodne mogą to zobaczyć
  • Publiczny: świat to widzi

Podczas dziedziczenia możesz (w niektórych językach) zmienić typ ochrony członka danych w określonym kierunku, np. Z chronionego na publiczny.

Roee Adler
źródło
6

Prywatny:

Dostęp do prywatnych członków klasy podstawowej mogą uzyskać tylko członkowie tej klasy podstawowej.

Publiczny:

Członkowie publiczni klasy bazowej mogą uzyskać dostęp do członków tej klasy bazowej, członków jej klasy pochodnej, a także członków spoza klasy podstawowej i klasy pochodnej.

Chroniony:

Dostęp do chronionych członków klasy podstawowej mogą mieć członkowie klasy podstawowej oraz członkowie jej klasy pochodnej.


W skrócie:

prywatny : baza

chroniony : podstawa + pochodny

public : baza + pochodna + dowolny inny członek

varun
źródło
5

Znalazłem łatwą odpowiedź i dlatego pomyślałem o opublikowaniu jej również w przyszłości.

Jest z linków http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/

class Base
{
public:
    int m_nPublic; // can be accessed by anybody
private:
    int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
    int m_nProtected; // can be accessed by Base member functions, or derived classes.
};

class Derived: public Base
{
public:
    Derived()
    {
        // Derived's access to Base members is not influenced by the type of inheritance used,
        // so the following is always true:

        m_nPublic = 1; // allowed: can access public base members from derived class
        m_nPrivate = 2; // not allowed: can not access private base members from derived class
        m_nProtected = 3; // allowed: can access protected base members from derived class
    }
};

int main()
{
    Base cBase;
    cBase.m_nPublic = 1; // allowed: can access public members from outside class
    cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
    cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}
Prajosh Premdas
źródło
3

Zasadniczo jest to ochrona dostępu publicznego i chronionych członków klasy podstawowej w klasie pochodnej. Dzięki dziedziczeniu publicznemu klasa pochodna może widzieć publicznych i chronionych członków bazy. W przypadku dziedziczenia prywatnego nie może. W trybie chronionym klasa pochodna i wszystkie klasy pochodne od niej mogą je zobaczyć.

Dan Olson
źródło