Dziedziczenie: „A” jest niedostępną podstawą „B”


82
$ cat inheritance.cpp 
#include <iostream>

using namespace std;

class A { };
class B : private A { };

int main() {
    A* ab = new B;
}
$
$ g++ inheritance.cpp
inheritance.cpp: In function 'int main()':
inheritance.cpp:9: error: 'A' is an inaccessible base of 'B'
$

Po prostu nie rozumiem tego błędu.

Jak rozumiem i jak potwierdza ten samouczek , privatedziedziczenie powinno zmieniać tylko sposób, w jaki członkowie class Bsą widoczni dla świata zewnętrznego.

Myślę, że prywatny specyfikator robi coś więcej niż tylko zmianę widoczności class Bczłonków tutaj.

  • Co otrzymuję ten błąd i co to znaczy?
  • Zasadniczo, co jest złego w dopuszczaniu tego typu kodu w C ++? Wygląda całkowicie nieszkodliwie.

Odpowiedzi:


100

Uczyniając dziedziczenie prywatnym, zasadniczo mówisz, że nawet fakt, że B dziedziczy po A (w ogóle) jest prywatny - nie jest dostępny / widoczny dla świata zewnętrznego.

Bez wdawania się w długotrwałą dyskusję na temat tego, co by się stało, gdyby było to dozwolone, prosty fakt jest taki, że jest to niedozwolone. Jeśli chcesz użyć wskaźnika do podstawy, aby odwołać się do obiektu typu pochodnego, utkniesz z wykorzystaniem dziedziczenia publicznego.

Prywatne dziedziczenie to nie koniecznie (lub nawet normalnie) przeznaczone do naśladowania zasadę substytucji Liskov . Dziedziczenie publiczne zapewnia, że ​​obiekt pochodny można zastąpić obiektem klasy bazowej, a właściwa semantyka nadal będzie wynikać. Dziedziczenie prywatne jednak tego nie potwierdza. Zwykły opis relacji implikowanej przez dziedziczenie prywatne jest „realizowany w kategoriach”.

Dziedziczenie publiczne oznacza, że ​​klasa pochodna zachowuje wszystkie możliwości klasy bazowej i potencjalnie dodaje więcej. Dziedziczenie prywatne często oznacza mniej więcej coś przeciwnego: klasa pochodna używa ogólnej klasy bazowej do implementacji czegoś z bardziej ograniczonym interfejsem.

Na przykład załóżmy na chwilę, że kontenery w standardowej bibliotece C ++ zostały zaimplementowane przy użyciu dziedziczenia, a nie szablonów. W obecnym systemie std::dequei std::vectorsą kontenerami i std::stackjest adapterem kontenera, który zapewnia bardziej ograniczony interfejs. Ponieważ jest oparty na szablonach, można go używać std::stackjako adaptera dla jednego std::dequelub std::vector.

Gdybyśmy chcieli zapewnić zasadniczo to samo z dziedziczeniem, prawdopodobnie użylibyśmy dziedziczenia prywatnego, więc std::stackbyłoby coś takiego:

class stack : private vector {
    // ...
};

W tym przypadku zdecydowanie nie chcemy, aby użytkownik mógł manipulować naszym plikiem stacktak, jakby był plikiem vector. Takie postępowanie mogłoby (i prawdopodobnie mogłoby) naruszyć oczekiwania stosu (np. Użytkownik mógłby wstawiać / wyjmować elementy w środku, a nie zgodnie z przeznaczeniem). Zasadniczo używamy vectorjako wygodnego sposobu implementacji naszego stosu, ale jeśli (na przykład) zmieniliśmy implementację na stacksamodzielną (bez zależności od klasy bazowej) lub ponownie zaimplementowaliśmy ją pod względem std::deque, nie chcemy tego wpływać na dowolny kod klienta - w kodzie klienta ma to być tylko stos, a nie jakaś wyspecjalizowana odmiana wektorów (lub deque).


1
dotyczy to równieżprotected
SubMachine

12

dziedziczenie prywatne powinno zmieniać jedynie sposób, w jaki członkowie klasy B są widoczni dla świata zewnętrznego

To robi. I jeśli

A* p = new B;

były dozwolone, wtedy odziedziczeni członkowie każdego z nich Bmożna było uzyskać ze świata zewnętrznego, po prostu tworząc plik A*. Ponieważ są one dziedziczone prywatnie, ten dostęp jest nielegalny, podobnie jak wyrzuty sumienia.


8

clang++ daje nieco bardziej zrozumiały komunikat o błędzie:

example.cpp:9:13: error: cannot cast 'B' to its private base class 'A'
    A* ab = new B;
            ^
example.cpp:6:11: note: declared private here
class B : private A { };
          ^~~~~~~~~
1 error generated.

Nie jestem ekspertem od C ++, ale wygląda na to, że jest to po prostu niedozwolone. Pójdę po specyfikacji i zobaczę, co wymyślę.

Edycja: oto odpowiednie odniesienie ze specyfikacji - Sekcja 4.10 Konwersje wskaźników , akapit 3:

Wartość pr typu „pointer to cv D ”, gdzie Djest typem klasy, można przekonwertować na wartość pr typu „pointer to cv B”, gdzie B jest klasą bazową D. Jeśli Bjest niedostępną lub niejednoznaczną klasą bazową programu D, program, który wymaga tej konwersji, jest źle sformułowany.


5

To całkiem proste: fakt, że Ajest dziedziczony prywatnie, oznacza, że ​​fakt, że się Brozciąga, Ajest tajemnicą i tylko ją B„zna”. To jest właśnie definicja prywatnego dziedziczenia.


4
Ale pojawia się bardzo ten sam błąd, jeśli zastąpi privatesię protected.
Lazer

2
W rzeczy samej. „Chroniony” oznacza, że ​​wiedza jest ograniczona do Bi podklasy (i przyjaciele) B. „ A* ab = new B;” byłby legalny w hipotetycznej klasie Cbędącej podklasą klasy B.
Ernest Friedman-Hill

3

Dziedziczenie prywatne oznacza, że ​​poza klasą pochodną informacje o dziedziczeniu są ukryte. Oznacza to, że nie można rzutować klasy pochodnej na klasę bazową: relacja nie jest znana wywołującemu.


Dzięki, ale to jakoś nie ma sensu. privateJedynym interesem powinno być kontrolowanie zachowania członków. Jaka byłaby szkoda, gdyby informacje o dziedziczeniu nie były ukryte?
Lazer

1
Dziedziczenie prywatne jest formą agregacji / kompozycji. Jest to sposób na posiadanie właściwości klasy bazowej, bez bycia obiektem klasy bazowej. Jeśli nie tego chcesz, prywatne dziedzictwo nie jest dla ciebie. Tak to po prostu działa.
tmpearce

0

To działa

#include <iostream>

using namespace std;

class A{
    public:
        virtual void update() = 0;
};

class B: public A{
    public:
    virtual void update(){std::cout<<"hello";};
};

int main()
{
    A *a = new B();

    a->update();

    return 0;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.