2010-11-07 20 views
18

Alerte spoiler: Peut-être une question stupide. :)'Base directe inaccessible' causée par l'héritage multiple

#include <iostream> 

using namespace std; 

class Base 
{ 
    public: 
     virtual void YourMethod(int) const = 0; 
}; 

class Intermediate : private Base 
{ 
    public: 
     virtual void YourMethod(int i) const 
     { 
      cout << "Calling from Intermediate" << i << "\n"; 
     } 
}; 

class Derived : private Intermediate, public Base 
{ 
    public: 
     void YourMethod(int i) const 
     { 
      cout << "Calling from Derived : " << i << "\n"; 
     } 
}; 

int main() 
{ 
} 

Quelqu'un peut-il Expliquez-moi pourquoi cela jette le compilateur avertissement:

main.cpp:21: warning: direct base ‘Base’ inaccessible in ‘Derived’ due to ambiguity 

Maintenant, je comprends qu'il n'y a aucun moyen de ce code ne fonctionnera. Je veux savoir pourquoi. Base est privé à Intermediate donc il ne devrait pas être visible à Derived à Intermediate. Alors d'où vient l'ambiguïté? En constructeur?

+0

ne est pas un compilateur de correction d'erreurs de son [juste un avertissement] (http://ideone.com/Pe0nK). –

+0

J'imagine qu'appeler Derived-> YourMethod (5) devrait être bien ... après tout, dans la Base et l'Intermédiaire, YuorMethod est virtuel, alors pourquoi ne pouvez-vous pas définir votre propre implémentation? Vous n'appelez d'aucune façon les fonctions de base, donc le tihng public privé ne devrait pas avoir d'importance? – thecoshman

+0

@Prasoon: Édité. – nakiya

Répondre

25

Ceci n'a rien à voir avec les fonctions prioritaires. Cela a à voir avec les conversions. Cela n'a vraiment rien à voir avec l'accessibilité (c'est-à-dire «privé» ou autre) directement. Voici un exemple plus simple

struct A { int a; }; 
struct B : A { }; 
struct C : B, A { }; // direct A can't be referred to! 

Vous pouvez vous référer à l'objet A indirecte en convertissant d'abord à B puis à A:

B *b = &somec; 
A *a = b; 

Vous ne pouvez pas faire comme avec le directe Un objet. Si vous essayez de convertir directement en A, il y aura deux possibilités. Il s'ensuit qu'il est impossible de faire référence aux membres de données non statiques de l'objet direct A en raison d'un objet Derived.

Notez que l'accessibilité est orthogonale à la visibilité. Quelque chose peut être accessible même s'il n'est pas visible (par exemple en y faisant référence par un nom qualifié), et quelque chose peut être visible même s'il n'est pas accessible. Même si toutes les dérivations ci-dessus étaient déclarées private, le problème apparaîtrait toujours: L'accès est vérifié en dernier - il n'influencera pas les règles de recherche ou de conversion de nom. De plus, n'importe qui peut convertir en une classe de base privée non ambiguë avec un comportement défini (le C++ Standard fait une exception pour cela) en utilisant un cast de style C, même si normalement l'accès ne serait pas autorisé. Et puis il y a toujours des amis et la classe elle-même qui pourrait librement se convertir.

+0

Comment faire référence à quelque chose par un nom qualifié peut-il servir d'exemple pour que quelque chose soit accessible même s'il n'est pas visible? – nakiya

+0

@nakiya 'class A {int a; void f() {int a;/* outer a n'est pas visible * /}}; 'mais vous pouvez vous y référer par' A :: a'. Après le 'a' local dans' f', le membre de la classe était accessible (parce que c'est la même classe), mais son nom était caché par le 'a' local. –

+0

Est-ce que votre analogie maintient les arbres d'héritage? Si j'hérite en privé 'B' de' A' et hérite publiquement 'C' de' B', alors 'A' ne devrait pas être visible par' C' (protégé le rendrait possible). Par conséquent, si j'hérite 'C' de' A' également, 'C' devrait idéalement ne voir que' A' pas 'B's' A'. Mais, je suis d'accord que ce n'est pas le cas. – nakiya

6

La réponse de Johannes couvre les faits de base. Mais il y a un peu plus. Alors, pensez à

struct Base 
{ 
    Base(int) {} 
    void foo() const {} 
}; 

struct Intermediate: Base 
{ 
    Intermediate(int x) 
     : Base(x) 
    {} 
}; 

struct Derived: Intermediate, Base 
{ 
    Derived(int x) 
     : Intermediate(x) 
     , Base(x)   // OK 
    {} 
}; 

int main() 
{ 
    Derived o(667); 
    o.foo();    // !Oops, ambiguous. 
    o.Base::foo();   // !Oops, still ambiguous. 
} 

Quand je compile je reçois, comme maintenant (après la réponse de Johannes), vous attendre,

 
C:\test> gnuc x.cpp 
x.cpp:15: warning: direct base 'Base' inaccessible in 'Derived' due to ambiguity 
x.cpp: In function 'int main()': 
x.cpp:25: error: request for member 'foo' is ambiguous 
x.cpp:4: error: candidates are: void Base::foo() const 
x.cpp:4: error:     void Base::foo() const 
x.cpp:26: error: 'Base' is an ambiguous base of 'Derived' 

C:\test> msvc x.cpp 
x.cpp 
x.cpp(15) : warning C4584: 'Derived' : base-class 'Base' is already a base-class of 'Intermediate' 
     x.cpp(2) : see declaration of 'Base' 
     x.cpp(7) : see declaration of 'Intermediate' 
x.cpp(25) : error C2385: ambiguous access of 'foo' 
     could be the 'foo' in base 'Base' 
     or could be the 'foo' in base 'Base' 
x.cpp(25) : error C3861: 'foo': identifier not found 

C:\test> _ 

Comment résoudre dépend si elle est bien avec un seul sous -objet de la classe Base (comme c'est le cas lorsque Base est une interface pure), ou Intermediate nécessite vraiment son propre sous-objet Base. Ce dernier cas, deux sous-objets Base, n'est probablement pas ce que vous voulez, mais si vous voulez alors alors une cure est d'introduire encore une autre classe intermédiaire, disons ResolvableBase.

Comme:

struct Base 
{ 
    Base(int) {} 
    void foo() const {} 
}; 

struct Intermediate: Base 
{ 
    Intermediate(int x) 
     : Base(x) 
    {} 
}; 

struct ResolvableBase: Base 
{ 
    ResolvableBase(int x): Base(x) {} 
}; 

struct Derived: Intermediate, ResolvableBase 
{ 
    Derived(int x) 
     : Intermediate(x) 
     , ResolvableBase(x) 
    {} 
}; 

int main() 
{ 
    Derived o(667); 
    o.ResolvableBase::foo(); // OK. 
} 

Dans le premier cas, où par exempleBase est une interface et un seul sous-objet Base est nécessaire, vous pouvez utiliser l'héritage virtuel.

L'héritage virtuel ajoute généralement un certain temps d'exécution, et Visual C++ ne l'aime pas trop.

Mais il vous permet de « héritez dans » une mise en œuvre d'une interface, comme en Java et C#:

struct Base 
{ 
    Base(int) {} 
    virtual void foo() const = 0; 
}; 

struct Intermediate: virtual Base 
{ 
    Intermediate(int x) 
     : Base(x) 
    {} 
    void foo() const {}  // An implementation of Base::foo 
}; 

struct Derived: virtual Base, Intermediate 
{ 
    Derived(int x) 
     : Base(x) 
     , Intermediate(x) 
    {} 
}; 

int main() 
{ 
    Derived o(667); 
    o.foo(); // OK. 
} 

Subtilité: J'ai changé la liste d'héritage ordre afin d'éviter g ++ sillywarnings sur l'ordre d'initialisation. Désagrément: Visual C++ émet des erreurs C4250 sur l'héritage (de l'implémentation) via la dominance. C'est comme "avertissement: vous utilisez une fonction principale standard". Eh bien, éteignez-le.

Vive & HTH.,

+0

Mon problème est, plutôt que d'utiliser Derived seul, quand vous l'utilisez via un handle de type 'Intermediate' (Dans ma question) il ne devrait y avoir aucun problème de résolution car' Intermediate' dérive en privé de 'Base'. – nakiya

+0

@nakiya: en supposant par "une poignée" vous voulez dire "une référence", eh bien je ne vois pas cela dans votre question, mais ce n'est tout simplement pas un problème alors. Le compilateur peut toujours avertir de la définition 'Derived'. Mais pourquoi héritez-vous directement de 'Base' dans' Derived'? Cheers, –

+0

Par "un handle" je veux dire "une référence ou un pointeur". Voir cette question (Et ma réponse) comme la réponse à votre deuxième question: http://stackoverflow.com/questions/4117538/how-to-be-sure-a-method-is-overriding-an-existing-virtual- un-en-c/4118483 # 4118483 – nakiya