Herencia: 'A' es una base inaccesible de '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'
$

Simplemente no entiendo este error.

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

Creo que el especificador privado está haciendo más que solo cambiar la visibilidad de los class Bmiembros aquí.

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

Respuestas:

100

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

Sin entrar en una larga discusión sobre lo que sucedería si estuviera permitido, el simple hecho es que no está permitido. Si desea usar un puntero a la base para referirse a un objeto de tipo derivado, entonces está bastante atrapado con el uso de herencia pública.

La herencia privada no necesariamente (ni siquiera normalmente) pretende 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 adecuada seguirá resultando. Sin embargo, la herencia privada no lo afirma. 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 usa una clase base general para implementar algo con una interfaz más restringida.

Por ejemplo, supongamos por el momento que los contenedores de la biblioteca estándar de C ++ se implementaron usando herencia en lugar de plantillas. En el sistema actual, std::dequey std::vectorson contenedores, y std::stackes un adaptador de contenedor que proporciona una interfaz más restringida. Dado que se basa en plantillas, puede utilizarlo std::stackcomo adaptador para std::dequeo std::vector.

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

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

En este caso, definitivamente no queremos que el usuario pueda manipular nuestro stackcomo si fuera un vector. Hacerlo podría (y probablemente lo haría) violar las expectativas de una pila (por ejemplo, el usuario podría insertar / eliminar elementos en el medio, en lugar de una forma puramente similar a una pila como se pretendía). Básicamente, lo estamos usando vectorcomo una forma conveniente de implementar nuestra pila, pero si (por ejemplo) cambiamos la implementación para que sea stackindependiente (sin dependencia de una clase base) o la reimplementamos en términos de std::deque, no queremos eso para afectar cualquier código de cliente: para el código de cliente, se supone que esto es solo una pila, no una variedad especializada de vector (o deque).

Jerry Coffin
fuente
1
esto también se aplica aprotected
SubMachine
12

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;

se permitieron, entonces se Bpodría acceder a los miembros heredados de cualquiera desde el mundo exterior, simplemente haciendo un A*. Dado que se heredan de forma privada, ese acceso es ilegal, y también lo es el upcast.

Ben Voigt
fuente
8

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. Iré a hurgar en la especificación 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 Des un tipo de clase, se puede convertir en un prvalue de tipo "puntero a cv B", donde B es una clase base de D. Si Bes una clase base inaccesible o ambigua de D, un programa que necesita esta conversión está mal formado.

Carl Norum
fuente
5

Es bastante simple: el hecho de que Ase herede de forma privada significa que el hecho de que se Bextiende Aes un secreto, y solo lo B"conoce". Esa es la definición misma de herencia privada.

Ernest Friedman-Hill
fuente
4
Pero obtengo el mismo error si reemplazo privatecon protected.
Lazer
2
En efecto. "Protegido" significa que el conocimiento se limita a las Bsubclases (y amigos) de B. " A* ab = new B;" sería legal en una clase hipotética Cque fuera una subclase de B.
Ernest Friedman-Hill
3

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 persona que llama no conoce la relación.

tmpearce
fuente
Gracias, pero de alguna manera no tiene sentido. privateEl único negocio debería ser controlar cómo se comportan los miembros. ¿Cuál sería el daño si la información de la herencia no se 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 quiere, la herencia privada no es para usted. Así es como funciona.
tmpearce
0

Esto esta funcionando

#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
fuente