2010-11-07 11 views
18

Spoileralarm: Vielleicht eine blöde Frage. :)'Unzugängliche direkte Basis' verursacht durch mehrfache Vererbung

#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() 
{ 
} 

Kann jemand mir erklären, warum dies der Compiler Warnung wirft:

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

Nun, ich verstehe, dass es keine Möglichkeit gibt, wird dieser Code arbeiten. Ich möchte wissen warum. Base ist privat zu Intermediate so sollte es nicht sichtbar sein Derived durch Intermediate. Woher kommt die Mehrdeutigkeit? Im Konstruktor?

+0

Es ist kein Compiler-Fehler seine [nur eine Warnung] (http://ideone.com/Pe0nK). –

+0

Ich würde mir vorstellen, dass der Aufruf von Derived-> YourMethod (5) in Ordnung sein sollte ... schließlich ist YuorMethod sowohl in Base als auch in Intermediate virtuell, also warum kannst du deine eigene Implementation nicht definieren. Sie rufen die Basisfunktionen nicht auf irgendeine Weise an, also sollte das öffentliche private TIHNG nicht wichtig sein? – thecoshman

+0

@ Prasoon: Bearbeitet. – nakiya

Antwort

25

Dies hat nichts mit übersteuernden Funktionen zu tun. Es hat mit Konvertierungen zu tun. Es hat auch nichts mit Barrierefreiheit (d. H. "Privat" oder so) direkt zu tun. Hier ist ein einfacheres Beispiel

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

Sie das indirekten A Objekt, indem zuerst auf B und dann auf A verweisen:

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

Sie nicht mit dem direkten A-Objekt so tun können. Wenn Sie versuchen, direkt zu A zu konvertieren, wird es zwei Möglichkeiten haben. Daraus folgt, dass es unmöglich ist, auf die nicht statischen Datenelemente des direkten Objekts A zu verweisen, das ein Objekt Derived erhält.

Beachten Sie, dass die Zugänglichkeit orthogonal zur Sichtbarkeit ist. Etwas kann zugänglich sein, auch wenn es nicht sichtbar ist (zum Beispiel durch Bezugnahme auf einen qualifizierten Namen), und etwas kann sichtbar sein, obwohl es nicht zugänglich ist. Selbst wenn alle oben genannten Ableitungen als private deklariert würden, würde das Problem immer noch auftauchen: Der Zugriff wird zuletzt überprüft - er hat keinen Einfluss auf die Namenssuche oder Konvertierungsregeln.

Außerdem kann jeder zu einer eindeutigen privaten Basisklasse mit definiertem Verhalten (der C++ - Standard macht eine Ausnahme dafür aus) eine C-Stil-Umwandlung verwenden, selbst wenn normalerweise kein Zugriff gewährt würde. Und dann gibt es noch Freunde und die Klasse selbst, die sich frei bekehren konnte.

+0

Wie kann die Bezugnahme auf etwas mit einem qualifizierten Namen ein Beispiel dafür sein, dass etwas zugänglich ist, auch wenn es nicht sichtbar ist? – nakiya

+0

@nakiya 'Klasse A {int a; void f() {int a;/* äußere a ist nicht sichtbar * /}}; 'aber Sie können sich darauf beziehen mit' A :: a'. Nach dem lokalen 'a' in' f' war das Klassenmitglied zugänglich (weil es dieselbe Klasse ist), aber sein Name wurde durch das lokale 'a' verdeckt. –

+0

Behält Ihre Analogie Vererbungsbäume? Wenn ich "B" privat von "A" vererbe und "C" öffentlich von "B" vererbe, dann sollte "A" für "C" nicht sichtbar sein (geschützt würde es möglich machen). Wenn ich also auch "C" von "A" erben sollte, sollte "C" im Idealfall nur sein "A" und nicht "B" 'A' sehen. Aber ich stimme zu, dass dies nicht der Fall ist. – nakiya

6

Johannes 'Antwort deckt die grundlegenden Fakten ab. Aber da ist noch ein bisschen mehr. So betrachten

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. 
} 

Wenn ich kompilieren ich, wie jetzt durch (nach Johannes' Antwort) Sie erwarten,

 
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> _ 

Wie hängt davon ab, zu klären, ob es in Ordnung ist mit einem einzigen Unter -Objekt der Klasse Base (wie es der Fall ist, wenn Base eine reine Schnittstelle ist) oder Intermediate erfordert wirklich ein eigenes Base Unterobjekt.

Der letzte Fall, zwei Base Unterobjekte, ist wahrscheinlich nicht das, was Sie wollen, aber wenn Sie das dann dann eine Heilung ist, um noch eine andere Zwischenklasse einzuführen, sagen wir ResolvableBase.

Like:

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. 
} 

Im ersten Fall, in dem z.B.Base ist eine Schnittstelle und nur ein Base Unterobjekt ist erforderlich, können Sie virtuelle Vererbung verwenden.

Die virtuelle Vererbung fügt in der Regel etwas Laufzeit-Overhead hinzu, und Visual C++ ist nicht allzu gern davon.

Aber es lässt Sie eine Implementierung einer Schnittstelle "in erben", wie in Java und 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. 
} 

Subtlety: Ich habe die Vererbungsliste bestellen, um g ++ sillywarnings über Initialisierungsreihenfolge zu vermeiden geändert.

Belästigung: Visual C++ gibt C4250 alberywarning über Vererbung (der Implementierung) über Dominanz aus. Es ist wie "Warnung: Sie verwenden eine Standard-Hauptfunktion". Naja, schalte es einfach aus.

Beifall & hth.,

+0

Mein Problem ist, anstatt Abgeleitet allein zu verwenden, wenn Sie es über ein Handle vom Typ 'Intermediate' verwenden (in meiner Frage) sollte es keine Auflösungsprobleme geben, da' Intermediate' von 'Base' abgeleitet wird. – nakiya

+0

@nakiya: Nehmen wir an "ein Handle", meinen Sie "eine Referenz", nun, ich sehe das nicht in Ihrer Frage, aber es ist einfach kein Problem. Der Compiler kann jedoch immer noch über die Definition "Derived" warnen. Aber warum erben Sie direkt von "Base" in "Derived"? Prost, –

+0

Mit "einem Griff" meine ich "eine Referenz oder einen Zeiger". Sehen Sie diese Frage (und meine Antwort) als Antwort auf Ihre zweite Frage: http://stackoverflow.com/questions/4117538/how-to-be-sure-a-methodis-is-overriding-an-existing-virtual- ein-in-c/4118483 # 4118483 – nakiya

Verwandte Themen