web-dev-qa-db-fra.com

Héritage: 'A' est une base inaccessible de 'B'

$ 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'
$

Je ne comprends tout simplement pas cette erreur.

Si je comprends bien, et comme ce tutoriel confirme, private l’héritage ne devrait changer que la manière dont les membres de class B sont visibles pour le monde extérieur.

Je pense que le spécificateur privé fait plus que changer la visibilité de class B membres ici.

  • Qu'est-ce que je reçois cette erreur et qu'est-ce que cela signifie?
  • En gros, qu'est-ce qui ne va pas avec l'autorisation de ce type de code en C++? Semble totalement inoffensif.
77
Lazer

En rendant l'héritage privé, vous dites essentiellement que même le fait que B hérite de A (du tout) est privé - non accessible/visible pour le monde extérieur.

Sans entrer dans une longue discussion sur ce qui se passerait si c'était autorisé, le simple fait est que ce n'est pas permis. Si vous souhaitez utiliser un pointeur à la base pour faire référence à un objet de type dérivé, vous êtes plutôt coincé dans l'utilisation de l'héritage public.

L'héritage privé est pas nécessairement (ou même normalement) destiné à suivre le principe de substitution de Liskov . L'héritage public affirme qu'un objet dérivé peut être substitué à un objet de la classe de base et que la sémantique appropriée sera toujours résultat. L'héritage privé n'affirme pas cela . La description habituelle de la relation impliquée par l'héritage privé est "est implémentée en termes de".

L'héritage public signifie qu'une classe dérivée maintient toutes les capacités de la classe de base et en ajoute potentiellement d'autres. L'héritage privé signifie souvent plus ou moins le contraire: que la classe dérivée utilise une classe de base générale pour implémenter quelque chose avec une interface plus restreinte.

Juste pour exemple, supposons pour le moment que les conteneurs de la bibliothèque standard C++ ont été implémentés en utilisant l'héritage plutôt que des modèles. Dans le système actuel, std::deque et std::vector sont des conteneurs et std::stack est un adaptateur de conteneur offrant une interface plus restreinte. Comme il est basé sur des modèles, vous pouvez utiliser std::stack comme adaptateur pour soit std::deque ou std::vector.

Si nous voulions fournir essentiellement la même chose avec l'héritage, nous utiliserions probablement l'héritage privé, donc std::stack serait quelque chose comme:

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

Dans ce cas, nous voulons absolument pas que l'utilisateur puisse manipuler notre stack comme s'il s'agissait d'un vector. Cela pourrait (et risquerait probablement) de violer les attentes d'une pile (par exemple, l'utilisateur pourrait insérer/supprimer des éléments au milieu, plutôt que de manière purement comme une pile, comme prévu). Nous utilisons essentiellement vector comme moyen pratique d’implémenter notre pile, mais si (par exemple) nous modifions l’implémentation pour stack autonome (sans dépendance d’une classe de base) ou si -implémentez-le en termes de std::deque, nous faisons pas voulons que cela affecte tout code client - pour le code client, ceci est supposé être juste une pile, pas une variété spécialisée de vecteur (ou deque).

94
Jerry Coffin

l'héritage privé ne devrait changer que la manière dont les membres de la classe B sont visibles au monde extérieur

Cela fait. Et si

A* p = new B;

ont été autorisés, puis les membres hérités de tout B peuvent être accédés du monde extérieur, simplement en faisant un A*. Étant donné qu'ils sont hérités en privé, cet accès est illégal, de même que la diffusion ascendante.

11
Ben Voigt

clang++ donne un message d'erreur légèrement plus compréhensible:

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.

Je ne suis pas un expert en C++, mais il semble que ce ne soit tout simplement pas autorisé. Je vais examiner les spécifications et voir ce que je propose.

Edit: voici la référence pertinente de la spécification - Section 4.10 Conversion de pointeurs, paragraphe 3:

Une valeur de type "pointeur sur cv D", où D est un type de classe, peut être converti en une valeur de type "pointeur sur cv B", où B est une classe de base de D. Si B est une classe de base inaccessible ou ambiguë de D, un programme nécessitant cette conversion est mal formé.

7
Carl Norum

C'est assez simple: le fait que A soit hérité de manière privée signifie que le fait que B s'étend A est un secret et que seul B "le sait" . C'est la définition même de l'héritage privé.

5

Héritage privé signifie qu'en dehors de la classe dérivée, les informations d'héritage sont masquées. Cela signifie que vous ne pouvez pas convertir la classe dérivée en classe de base: la relation n'est pas connue de l'appelant.

3
tmpearce