Herencia: ‘A’ es una base inaccesible de ‘B’

$ cat inheritance.cpp #include  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' $ 

Simplemente no entiendo este error.

Según tengo entendido, y como confirma este tutorial , private herencia private solo debería cambiar la forma en que los miembros de la class B son visibles para el mundo exterior.

Creo que el especificador privado está haciendo algo más que cambiar la visibilidad de class B miembros de la class B aquí.

  • ¿Qué obtengo de este error y qué significa?
  • Básicamente, ¿qué hay de malo en permitir este tipo de código en C ++? Parece totalmente inofensivo.

Al hacer que la herencia sea privada, básicamente está diciendo que incluso el hecho de que B hereda de A (en absoluto) es privado, no accesible / visible para el mundo exterior.

Sin entrar en una discusión prolija de lo que sucedería si se permitiera, el simple hecho es que no está permitido. Si desea utilizar un puntero a la base para referirse a un objeto de tipo derivado, entonces está prácticamente atrapado en el uso de la herencia pública.

Editar: Desde que alguien se tomó la molestia de enviar un correo electrónico para pedir más información sobre lo que podría pasar si esto estaba permitido, supongo que elaboraré un poco sobre él.

El problema básico es que la herencia privada no necesariamente tiene la intención de seguir el principio de sustitución de Liskov . La herencia pública afirma que un objeto derivado se puede sustituir por un objeto de la clase base, y la semántica propia todavía resultará. La herencia privada no afirma eso sin embargo. La descripción habitual de la relación implícita en la herencia privada es “se implementa en términos de”.

La herencia pública significa que una clase derivada mantiene todas las capacidades de la clase base y potencialmente agrega más. La herencia privada a menudo significa más o menos lo contrario: que la clase derivada utiliza una clase base general para implementar algo con una interfaz más restringida.

Solo por ejemplo, supongamos por el momento que los contenedores en la biblioteca estándar de C ++ se implementaron usando herencia en lugar de plantillas. En el sistema actual, std::deque y std::vector son contenedores, y std::stack es un adaptador de contenedor que proporciona una interfaz más restringida. Como está basado en plantillas, puede usar std::stack como un adaptador para std::deque o std::vector .

Si quisiéramos proporcionar esencialmente lo mismo con la herencia, probablemente usaríamos la herencia privada, por lo que std::stack sería algo así como:

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

En este caso, definitivamente no queremos que el usuario pueda manipular nuestra stack como si fuera un vector . Hacerlo podría (y probablemente) violar las expectativas de una stack (por ejemplo, el usuario podría insertar / eliminar elementos en el medio, en lugar de una manera puramente astackda según lo previsto). Básicamente utilizamos el vector como una forma conveniente de implementar nuestra stack, pero si (por ejemplo) cambiamos la implementación para la stack sola (sin dependencia de una clase base) o la implementamos en términos de std::deque , no queremos que esto afecte a ningún código de cliente: al código de cliente, se supone que es solo una stack, no una variedad especializada de vector (o deque).

la herencia privada solo debería cambiar la forma en que los miembros de la clase B son visibles para el mundo exterior

Lo hace. Y si

 A* p = new B; 

fueron permitidos, entonces se podía acceder a los miembros heredados de cualquier B desde el mundo exterior, simplemente haciendo un A* . Como se heredan de forma privada, ese acceso es ilegal, y también lo es el aumento.

clang++ da un mensaje de error un poco más comprensible:

 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. 

No soy un experto en C ++, pero parece que simplemente no está permitido. Voy a hurgar alrededor de las especificaciones y ver qué se me ocurre.

Editar: aquí está la referencia relevante de la especificación – Sección 4.10 Conversiones de puntero , párrafo 3:

Un prvalue de tipo “puntero a cv D “, donde D es un tipo de clase, se puede convertir a un prvalue de tipo “puntero a cv B “, donde B es una clase base de D Si B es una clase base inaccesible o ambigua de D , un progtwig que necesita esta conversión está mal formado.

Es bastante simple: el hecho de que A se herede en privado significa que el hecho de que B extienda A es un secreto, y solo B “conoce”. Esa es la definición misma de herencia privada.

La herencia privada significa que, fuera de la clase derivada, la información de herencia está oculta. Eso significa que no puede convertir la clase derivada a la clase base: la relación no es conocida por la persona que llama.