2012-04-15 4 views
4

sieht es vollkommen sicher ein void(Derived::*)() einen void(Base::*)(), wie in diesem Code zu werfen:Warum kann ich einen Zeiger auf Abgeleitete Klassenmitgliedsfunktion nicht auf dieselbe Klasse der Klasse Base umwandeln? Für mich

#include <iostream> 
#include <typeinfo> 
using namespace std; 
struct Base{ 
    void(Base::*any_method)(); 
    void call_it(){ 
     (this->*any_method)(); 
    } 
}; 
struct Derived: public Base{ 
    void a_method(){ 
     cout<<"method!"<<endl; 
    } 
}; 
int main(){ 
    Base& a=*new Derived; 
    a.any_method=&Derived::a_method; 
    a.call_it(); 
} 

Aber der Compiler beschwert sich über die Besetzung bei a.any_method=&Derived::a_method;. Ist das ein Hindernis, um subtile Programmierfehler zu vermeiden, oder einfach etwas, das Compilerautoren das Leben leichter macht? Gibt es Problemumgehungen, die Klasse Base einen Zeiger auf Elementfunktionen von Derivedohne Typ Knoweledge (das heißt, ich kann nicht Base eine Vorlage mit Vorlage Argument Derived) haben.

Antwort

6

Was passiert, wenn Ihre Derived::a_method() versucht ein Datenelement nur in Derived vorhanden zu verwenden, nicht in Base, und rufen Sie es auf einem Base Objekt (oder ein Objekt abgeleitet von Base aber nicht im Zusammenhang mit Derived)?

Die Umstellung umgekehrt macht Sinn, diese nicht.

3

Nein, es ist potentiell gefährlich. Eine abgeleitete Klassenfunktion kann alle abgeleiteten Klasseneigenschaften von *this verwenden. Ein Zeiger auf die Basisklassenfunktion kann für jede Basisklasseninstanz aufgerufen werden, auch für solche, die nicht vom abgeleiteten Typ sind.

Der Zugriff auf die abgeleiteten Klasseneigenschaften einer Instanz, die keine abgeleitete Klasse ist, wird nicht funktionieren, daher ist das Umwandeln eines Verweises auf die abgeleitete Klassenfunktion auf einen Zeiger auf den Basisklassenzeiger korrekt nicht zulässig.

Auf der anderen Seite ist es sicher und legal, einen Zeiger auf die Basisklassenfunktion auf einen Zeiger auf die abgeleitete Klassenfunktion zu übertragen.

1

Sie müssen eine std::function<void()> verwenden. Dies kann jedes Mitglied einer Klasse sein, ein Lambda, eine freie Funktion, ein Funktionsobjekt, was auch immer Sie brauchen, was sehr praktisch ist.

#include <iostream> 
#include <typeinfo> 
using namespace std; 
struct Base{ 
    std::function<void()> any_method; 
    void call_it(){ 
     any_method(); 
    } 
}; 
struct Derived: public Base{ 
    void a_method(){ 
     cout<<"method!"<<endl; 
    } 
}; 
int main(){ 
    Derived* d = new Derived; 
    Base& a= *d; 
    a.any_method = [d] { d->a_method(); }; 
    a.call_it(); 
} 

Hier können Sie sehen, dass die tatsächliche Umsetzung von any_method ist völlig von struct Base abstrahiert, und ich kann ein Funktionsobjekt liefern, die alles tut, um all-inklusive bequem eine abgeleitete Methode aufrufen.

+1

Es ist nicht sehr klar, wie 'std :: function' hier helfen kann:' std :: function 'hat das gleiche Problem und wenn man' any_method' den Typ des Members gibt Funktion, dann hat es keinen Sinn, 'std :: function' statt eines einfachen freien Funktionszeigers zu verwenden. Recht? –

+0

@Lorenzo: Nr. 'Std :: Funktion '. Die Funktion kann ihren eigenen "This" -Zeiger speichern. Außerdem gibt es immer einen Punkt auf "std :: function" über einen Funktionszeiger - stateful lambdas, zum Beispiel das Ergebnis von "std :: bind". Ich habe meine Antwort bearbeitet, um deutlicher zu machen, wie 'std :: function' das Problem sofort löst. – Puppy

0

Ich kann mir vorstellen, dass dies etwas überraschend sein kann. Es macht jedoch Sinn, wenn Sie darüber nachdenken.

Damit eine Umwandlung zwischen zwei Typen automatisch erfolgt, sollte die folgende Beziehung gelten: Jede Instanz des ersten Typs sollte in der zweiten darstellbar sein.

Zum Beispiel, wenn d eine Instanz von Derived ist, dann kann er automatisch als Base& gegossen werden, da jede Instanz von Derived ist auch eine Instanz von Base. Dies ist Vererbung.

Nun, wenn es um Zeiger auf Member-Funktionen geht, ist die Beziehung tatsächlich umgekehrt. Es wird offensichtlich sein, dass irgendeine Methode von Base für eine Instanz von Derived existiert, aber das Gegenteil ist nicht richtig. Nach dem ganzen Sinn des Ableitens ist es, diese Notiz öfter um neue Funktionen zu ergänzen.

Eine andere Möglichkeit, dies zu visualisieren, ist die Verwendung von freien Funktionen. this ist nur ein impliziter Parameter in regulärer Funktion, wenn wir es explizit zu machen, erhalten wir:

void [email protected]_it(Base& self); 

void [email protected]_method(Derived& self); 

Nun, wenn ich zwei Instanzen d vom Typ habe Derived und b vom Typ Base dann:

Letzteres könnte [email protected]_method(dynamic_cast<Derived&>(b)) sein, aber dies führt eine Laufzeitprüfung ein, um die Eigenschaft tatsächlich zu überprüfen. Statisch ist es nicht entscheidbar.

Verwandte Themen