$ 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.
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).
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.
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 cvB
", où B est une classe de base deD
. SiB
est une classe de base inaccessible ou ambiguë deD
, un programme nécessitant cette conversion est mal formé.
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é.
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.