2010-11-07 14 views
18

Uwaga spoiler: Może głupie pytanie. :)"Niedostępna bezpośrednia podstawa" spowodowana wielokrotnym dziedziczeniem

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

Może ktoś Wyjaśnić mi, dlaczego to rzuca kompilator ostrzeżenie:

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

Teraz rozumiem, że nie ma sposobu, ten kod zadziała. Chcę wiedzieć dlaczego. Base jest prywatne dla Intermediate, więc nie powinno być widoczne dla Derived do Intermediate. Skąd się bierze niejednoznaczność? W konstruktorze?

+0

To nie błąd kompilatora jego [tylko ostrzeżenie] (http://ideone.com/Pe0nK). –

+0

Wyobrażam sobie, że wywoływanie Derived-> YourMethod (5) powinno być w porządku ... w końcu, zarówno w Bazie, jak i Intermediate, YuorMethod jest wirtualny, więc dlaczego nie możesz zdefiniować własnej implozji. Nie odwołujesz się do funkcji bazowych w żaden sposób, więc publiczny prywatny tihng nie powinien mieć znaczenia? – thecoshman

+0

@Prasoon: Edytowano. – nakiya

Odpowiedz

25

Nie ma to nic wspólnego z nadpisywaniem funkcji. Ma to związek z konwersjami. Nie ma to bezpośredniego związku z ułatwieniami dostępu (np. "Prywatnymi"). Tutaj jest prostszym przykładem

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

Można odwołać się do pośredniego A obiektu najpierw konwersja do B a następnie A:

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

nie można zrobić takie z bezpośrednim obiektu. Jeśli spróbujesz bezpośrednio przekonwertować na A, będzie to miało dwie możliwości. Wynika z tego, że niemożliwe jest odwoływanie się do niestatycznych elementów danych bezpośredniego obiektu A, biorąc pod uwagę obiekt Derived.

Należy zauważyć, że dostępność jest prostopadła do widoczności. Coś może być dostępne nawet wtedy, gdy nie jest widoczne (na przykład poprzez odniesienie się do niego kwalifikowaną nazwą), a coś może być widoczne, nawet jeśli nie jest dostępne. Nawet jeśli wszystkie powyższe wyprowadzenia zostałyby zadeklarowane jako private, problem nadal będzie się pojawiał: Dostęp jest sprawdzany jako ostatni - nie będzie miał wpływu na wyszukiwanie nazwy ani reguły konwersji.

Ponadto każdy może rzutować na jednoznaczną prywatną klasę bazową o określonym działaniu (standard C++ robi wyjątek) używając rzutowania w stylu C, nawet jeśli normalny dostęp nie zostałby przyznany. A potem są jeszcze przyjaciele i klasa, która może się swobodnie nawrócić.

+0

W jaki sposób odwoływanie się do czegoś pod kwalifikowaną nazwą może być przykładem tego, że coś jest dostępne, nawet jeśli nie jest widoczne? – nakiya

+0

@nakiya 'class A {int a; void f() {int a;/* zewnętrzna a nie jest widoczna * /}}; 'ale możesz odwołać się do niej przez' A :: a'. Po lokalnym 'a' w' f' element klasy był dostępny (ponieważ jest to ta sama klasa), ale jego nazwa została ukryta przez lokalny 'a'. –

+0

Czy twoja analogia trzyma drzewa dziedziczenia? Jeśli prywatnie odziedziczę 'B' z' A' i publicznie dziedziczę 'C' z' B', to 'A' nie powinno być widoczne dla' C' (chroniony mógłby to umożliwić). Dlatego też, jeśli dziedziczę 'C' z' A' również, 'C' powinien idealnie widzieć tylko jego' A', a nie 'B''' A'. Ale zgadzam się, że tak nie jest. – nakiya

6

Odpowiedź Johannesa obejmuje podstawowe fakty. Ale jest w tym trochę więcej. Tak więc, należy rozważyć

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

Kiedy kompilacji otrzymuję, jak teraz (po Johannes' odpowiedź) będziesz oczekiwać,

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

Jak rozwiązać zależy od tego, czy to jest w porządku z jednym sub -obiekt klasy Base (tak jak w przypadku, gdy Base jest czystym interfejsem), lub Intermediate naprawdę wymaga własnego sub-obiektu Base.

tym drugim przypadku dwa Base podrzędne obiekty, prawdopodobnie nie jest to, co chcesz, ale jeśli chcesz, aby następnie następnie jedno lekarstwo jest przedstawić kolejną klasę pośrednią, powiedzmy, ResolvableBase.

jak:

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

W pierwszym przypadku, gdy npBase jest interfejsem i wymagany jest tylko jeden sub-obiekt Base, można używać dziedziczenia wirtualnego.

Dziedziczenie wirtualne zazwyczaj zwiększa obciążenie środowiska wykonawczego, a program Visual C++ go nie lubi.

Ale to pozwala „dziedziczą w” implementacji interfejsu, jak Java i 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. 
} 

Subtelność: Zmieniłem dziedziczenia listy zamówienia w celu uniknięcia g ++ sillywarnings o kolejności inicjalizacji.

Podrażnienie: problemy z Visual C++ C4250 dotyczące dziedziczenia (implementacji) poprzez dominację. To jest jak "ostrzeżenie: używasz standardowej funkcji głównej". No cóż, po prostu wyłącz to.

Cheers & HTH.,

+0

Mój problem polega raczej na tym, że zamiast używać samego Pochodny, kiedy używam go przez uchwyt typu "Średni" (w moim pytaniu), nie powinno być problemów z rozdzielczością, ponieważ "Pośredni" pochodzi prywatnie z 'Bazy'. – nakiya

+0

@nakiya: zakładając "uchwyt" masz na myśli "odniesienie", cóż, nie widzę tego w twoim pytaniu, ale to po prostu nie jest problem. Kompilator może jednak nadal ostrzegać o definicji "pochodnej". Ale dlaczego dziedziczysz bezpośrednio z 'Base' w' Derived'? Pozdrawiam, –

+0

Przez "uchwyt" rozumiem "odniesienie lub wskaźnik". Zobacz to pytanie (I moja odpowiedź) jako odpowiedź na twoje drugie pytanie: http://stackoverflow.com/questions/4117538/how-to-be-sure-a-method-is-overriding-an-existing-virtual- jeden w c/4118483 # 4118483 – nakiya

Powiązane problemy