2012-03-12 7 views
66
$ 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' 
$ 

Simplemente no entiendo este error.Herencia: 'A' es una base inaccesible de 'B'

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

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

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

Respuesta

82

Al hacer que la herencia sea privada, básicamente dice 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 larga de lo que sucedería si se permitiera, el 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 se permitiera, supongo que elaboraré un poco sobre él.

El problema básico es que la herencia privada es no necesariamente destinada a seguir el Liskov substitution principle. La herencia pública afirma que un objeto derivado puede sustituirse por un objeto de la clase base, y la semántica propia dará como resultado. La herencia privada no no afirman eso sin embargo. La descripción habitual de la relación implícita en la herencia privada es "se implementa en términos de".

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. Dado que está basado en plantillas, puede usar std::stack como un adaptador para std::deque o std::vector.

Si quisiéramos proporcionar esencialmente la misma con la herencia, probablemente hubiéramos utilizar la herencia privada, por lo std::stack sería algo así como:

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

En este caso, no cabe duda hacemos no desea que el usuario ser capaz de manipular nuestro stack como si fuera un vector. Hacerlo podría (y probablemente) violar las expectativas de una pila (por ejemplo, el usuario podría insertar/quitar elementos en el medio, en lugar de una manera puramente apilada según lo previsto). Básicamente estamos usando vector como una forma conveniente de implementar nuestra pila, pero si (por ejemplo) cambiamos la implementación para stack por separado (sin dependencia de una clase base) o la implementamos en términos de std::deque, lo hacemos no quiere que afecte a cualquier código de cliente: para el código de cliente, se supone que es solo una pila, no una variedad especializada de vector (o deque).

6

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 experto en C++, pero parece que es simplemente no está permitido. Voy a hurgar alrededor de las especificaciones y ver qué se me ocurre.

Editar: aquí está la referencia correspondiente a la especificación - Sección 4.10 conversiones Pointer, párrafo 3:

Un prvalue del tipo "puntero a cvD", 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 programa que necesita esta conversión está mal formado.

3

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.

+0

Gracias, pero de alguna manera no tiene sentido. 'El único negocio de 'privado' debe ser controlar cómo se comportan los miembros. ¿Cuál sería el daño si la información de herencia no está oculta? – Lazer

+1

La herencia privada es una forma de agregación/composición. Es una forma de * tener * las propiedades de una clase base, sin * ser * un objeto de la clase base. Si eso no es lo que quieres, la herencia privada no es para ti. Así es como funciona. – tmpearce

5

Es bastante simple: el hecho de que A se hereda de forma privada significa que el hecho de que B se extiende A es un secreto, y sólo B "conoce" la misma. Esa es la definición misma de herencia privada.

+2

Pero obtengo el mismo error si reemplazo 'private' con' protected'. – Lazer

+1

De hecho. "Protegido" significa que el conocimiento está limitado a 'B' y subclases (y amigos) de' B'. "' A * ab = new B; '" sería legal en una clase hipotética 'C' que era una subclase de' B'. –

8

herencia privada sólo debe cambiar la forma de los miembros de la clase B son visibles para el mundo exterior

lo hace. Y si

A* p = new B; 

se les permitió, a continuación, los miembros heredados de cualquier B se puede acceder desde el mundo exterior, con sólo hacer un A*. Como se heredan de forma privada, ese acceso es ilegal, y también lo es el aumento.

Cuestiones relacionadas