Vererbung: 'A' ist eine unzugängliche Basis von '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'
$

Ich verstehe diesen Fehler einfach nicht.

Wie ich verstehe und wie dieses Tutorial bestätigt, privatesollte die Vererbung nur ändern, wie die Mitglieder von class Bfür die Außenwelt sichtbar sind.

Ich denke, der private Spezifizierer ändert class Bhier nicht nur die Sichtbarkeit der Mitglieder.

  • Was bekomme ich diesen Fehler und was bedeutet er?
  • Was ist grundsätzlich falsch daran, diese Art von Code in C ++ zuzulassen? Sieht völlig harmlos aus.
Laser
quelle

Antworten:

100

Indem Sie die Vererbung privat machen, sagen Sie im Grunde, dass selbst die Tatsache, dass B (überhaupt) von A erbt, privat ist - für die Außenwelt nicht zugänglich / sichtbar.

Ohne in eine langwierige Diskussion darüber zu geraten, was passieren würde, wenn es erlaubt wäre, ist die einfache Tatsache, dass es nicht erlaubt ist. Wenn Sie einen Zeiger auf die Basis verwenden möchten, um auf ein Objekt vom abgeleiteten Typ zu verweisen, müssen Sie die öffentliche Vererbung nicht mehr verwenden.

Private Erbschaft soll nicht unbedingt (oder sogar normalerweise) dem Liskov-Substitutionsprinzip folgen . Durch die öffentliche Vererbung wird bestätigt, dass ein abgeleitetes Objekt durch ein Objekt der Basisklasse ersetzt werden kann und sich dennoch eine ordnungsgemäße Semantik ergibt. Private Vererbung behauptet dies jedoch nicht . Die übliche Beschreibung der Beziehung, die durch private Vererbung impliziert wird, lautet "wird in Bezug auf implementiert".

Öffentliche Vererbung bedeutet, dass eine abgeleitete Klasse alle Funktionen der Basisklasse beibehält und möglicherweise weitere Funktionen hinzufügt. Private Vererbung bedeutet oft mehr oder weniger das Gegenteil: Die abgeleitete Klasse verwendet eine allgemeine Basisklasse, um etwas mit einer eingeschränkteren Schnittstelle zu implementieren.

Nehmen wir zum Beispiel an, dass die Container in der C ++ - Standardbibliothek eher mit Vererbung als mit Vorlagen implementiert wurden. Im aktuellen System sind std::dequeund std::vectorContainer und std::stackist ein Containeradapter, der eine eingeschränktere Schnittstelle bietet. Da es auf Vorlagen basiert, können Sie es std::stackals Adapter für entweder std::dequeoder verwenden std::vector.

Wenn wir bei der Vererbung im Wesentlichen dasselbe bereitstellen möchten, würden wir wahrscheinlich die private Vererbung verwenden, also std::stackwäre dies etwa:

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

In diesem Fall möchten wir definitiv nicht, dass der Benutzer unsere manipulieren kann, stackals ob es eine wäre vector. Dies könnte (und würde wahrscheinlich) die Erwartungen eines Stapels verletzen (z. B. könnte der Benutzer Elemente in der Mitte einfügen / entfernen, anstatt wie beabsichtigt rein stapelartig). Grundsätzlich verwenden wir vectoreine bequeme Möglichkeit, unseren Stack zu implementieren, aber wenn wir (zum Beispiel) die Implementierung für stackStandalone (ohne Abhängigkeit von einer Basisklasse) geändert oder in Bezug auf neu implementiert std::dequehaben , möchten wir das nicht Um einen Client-Code zu beeinflussen - für den Client-Code sollte dies nur ein Stapel sein, keine spezielle Art von Vektor (oder Deque).

Jerry Sarg
quelle
1
Dies gilt auch fürprotected
SubMachine
12

Private Vererbung sollte nur ändern, wie die Mitglieder der Klasse B für die Außenwelt sichtbar sind

Es tut. Und wenn

A* p = new B;

wurden erlaubt, dann konnte auf die geerbten Mitglieder von jedem Bvon der Außenwelt zugegriffen werden, indem einfach ein gemacht wurde A*. Da sie privat geerbt werden, ist dieser Zugriff illegal, ebenso wie der Upcast.

Ben Voigt
quelle
8

clang++ gibt eine etwas verständlichere Fehlermeldung:

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.

Ich bin kein C ++ - Experte, aber es sieht so aus, als ob es einfach nicht erlaubt ist. Ich werde mich in der Spezifikation umsehen und sehen, was mir einfällt.

Bearbeiten: Hier ist die relevante Referenz aus der Spezifikation - Abschnitt 4.10 Zeigerkonvertierungen , Absatz 3:

Ein Wert vom Typ "Zeiger auf Lebenslauf D ", wobei Des sich um einen Klassentyp handelt, kann in einen Wert vom Typ "Zeiger auf Lebenslauf B" konvertiert werden , wobei B eine Basisklasse von ist D. Wenn Bes sich um eine unzugängliche oder mehrdeutige Basisklasse handelt D, ist ein Programm, das diese Konvertierung erfordert, fehlerhaft.

Carl Norum
quelle
5

Es ist ziemlich einfach: Die Tatsache, dass Aprivat vererbt wird, bedeutet, dass die Tatsache, die Bsich Aausdehnt, ein Geheimnis ist und es nur B"weiß". Das ist genau die Definition von privater Vererbung.

Ernest Friedman-Hill
quelle
4
Aber ich habe den gleichen Fehler , wenn ich ersetzen privatemit protected.
Lazer
2
Tatsächlich. "Geschützt" bedeutet, dass das Wissen auf Bund Unterklassen (und Freunde) von beschränkt ist B. " A* ab = new B;" wäre legal in einer hypothetischen Klasse C, die eine Unterklasse von war B.
Ernest Friedman-Hill
3

Private Vererbung bedeutet, dass außerhalb der abgeleiteten Klasse die Vererbungsinformationen ausgeblendet sind. Das heißt, Sie können die abgeleitete Klasse nicht in die Basisklasse umwandeln: Die Beziehung ist dem Aufrufer nicht bekannt.

tmpearce
quelle
Danke, aber irgendwie macht es keinen Sinn. privateDas einzige Geschäft sollte darin bestehen, das Verhalten der Mitglieder zu kontrollieren. Was wäre der Schaden, wenn die Vererbungsinformationen nicht verborgen wären?
Lazer
1
Private Vererbung ist eine Form der Aggregation / Zusammensetzung. Es ist ein Weg zu haben , die Eigenschaften einer Basisklasse, ohne zu sein ein Objekt der Basisklasse. Wenn Sie das nicht möchten, ist die private Vererbung nichts für Sie. So funktioniert es einfach.
tmpearce
0

Das funktioniert

#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;
}
Deniz Babat
quelle