2010-11-07 9 views
18

Avviso spoiler: forse una domanda stupida. :)'Base diretta inaccessibile' causata da ereditarietà multipla

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

Qualcuno può spiegare a me perché questo getta il compilatore avvertimento:

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

Ora, capisco che non c'è modo questo codice funzionerà. Voglio sapere perché. Base è privato per Intermediate quindi non dovrebbe essere visibile a Derived tramite Intermediate. Quindi da dove viene l'ambiguità? Nel costruttore?

+0

suo non è un errore del compilatore suo [solo un avvertimento] (http://ideone.com/Pe0nK). –

+0

Immagino che chiamare Derived-> YourMethod (5) dovrebbe andare bene ... dopotutto, sia in Base che in Intermediate, YuorMethod è virtuale, quindi perché non puoi definire la tua implosione. Non stai chiamando le funzioni di base in alcun modo, quindi il tihng privato pubblico non dovrebbe avere importanza? – thecoshman

+0

@Prasoon: modificato. – nakiya

risposta

25

Questo non ha nulla a che fare con le funzioni di override. Ha a che fare con le conversioni. In realtà non ha a che fare direttamente con l'accessibilità (cioè "privata" o simile). Ecco un esempio più semplice

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

È possibile fare riferimento al A oggetto indiretto convertendo prima a B e poi a A:

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

Non si può fare ad esempio con la diretta Un oggetto. Se provi a convertire direttamente in A, avrà due possibilità. Ne consegue che è impossibile fare riferimento ai membri di dati non statici dell'oggetto A diretto dato un oggetto Derived.

Si noti che l'accessibilità è ortogonale alla visibilità. Qualcosa può essere accessibile anche se non è visibile (ad esempio facendo riferimento ad esso con un nome qualificato) e qualcosa può essere visibile anche se non è accessibile. Anche se tutte le suddette derivazioni sarebbero state dichiarate private, il problema si presenterebbe ancora: L'accesso è verificato per ultimo - non influenzerà la ricerca del nome o le regole di conversione.

Inoltre, chiunque può eseguire il cast su una classe base privata non ambigua con un comportamento definito (lo standard C++ fa un'eccezione per questo) utilizzando un cast in stile C, anche se normalmente l'accesso non sarebbe garantito. E poi ci sono ancora amici e la stessa classe che potrebbe convertirsi liberamente.

+0

Come può riferirsi a qualcosa con un nome qualificato essere un esempio per qualcosa che è accessibile anche se non è visibile? – nakiya

+0

@nakiya 'classe A {int a; void f() {int a;/* outer a non è visibile * /}}; 'ma puoi farvi riferimento da' A :: a'. Dopo il locale 'a' in' f', il membro della classe era accessibile (perché è la stessa classe), ma il suo nome era nascosto dal 'a' locale. –

+0

La tua analogia mantiene gli alberi ereditari? Se ereditassi privatamente 'B' da' A' e ereditassi pubblicamente 'C' da' B', quindi 'A' non dovrebbe essere visibile a' C' (protetto lo renderebbe possibile). Pertanto, se ereditario 'C' da' A' anche, 'C' dovrebbe idealmente vedere solo il suo' A' non 'B' s 'A'. Ma sono d'accordo che non è così. – nakiya

6

La risposta di Johannes copre i fatti di base. Ma c'è un po 'di più. Così, in considerazione

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

Quando compilo ricevo, come ormai (dopo Johannes' risposta) ti aspetta,

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

Come risolvere dipende dal fatto che è tutto a posto con un unico sub -oggetto della classe Base (come nel caso in cui Base è un'interfaccia pura) o Intermediate richiede realmente il proprio oggetto secondario Base.

L'ultimo caso, due sottooggetti Base, non è probabilmente quello che si desidera, ma se si desidera che quindi una cura è di introdurre ancora un'altra classe intermedia, ad esempio, ResolvableBase.

piace:

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

Nel primo caso, in cui ad esempioBase è un'interfaccia e è necessario un solo oggetto secondario Base, è possibile utilizzare l'ereditarietà virtuale.

L'ereditarietà virtuale in genere aggiunge un sovraccarico di runtime e Visual C++ non è troppo appassionato di esso.

Ma consente di "ereditano in" un'implementazione di un'interfaccia, come in Java e 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: ho cambiato l'ordine di lista di successione al fine di evitare g ++ sillywarnings su ordine di inizializzazione.

Annoyance: Visual C++ emette un C450 sillywarning sull'ereditarietà (dell'implementazione) tramite il dominio. È come "avviso: stai usando una funzione principale standard". Oh bene, spegnilo.

Acclamazioni & hth.,

+0

Il mio problema è, piuttosto che usare Derivato da solo, quando lo usi attraverso un handle di tipo 'Intermedio' (nella mia domanda) non ci dovrebbero essere problemi di risoluzione perché' Intermediate' deriva privatamente da 'Base'. – nakiya

+0

@nakiya: assumendo per "una maniglia" intendi "un riferimento", beh non riesco a vederlo nella tua domanda, ma non è un problema allora. Il compilatore può comunque avvertire della definizione "Derivato". Ma perché stai ereditando direttamente da "Base" in "Derivato"? Cheers, –

+0

Con "un handle" intendo "un riferimento o un puntatore". Vedi questa domanda (e la mia risposta) come la risposta alla tua seconda domanda: http://stackoverflow.com/questions/4117538/how-to-be-sure-a-method-is-overriding-an-existing-virtual- uno su c/4118483 # 4118483 – nakiya

Problemi correlati