2008-10-25 16 views
6

Hier ist mein Problem: Ich habe eine virtuelle Methode in einer .h-Datei definiert, die ich in einer Klasse aufrufen möchte, die von der Basisklasse erbt. Leider wird die Methode in der abgeleiteten Klasse nicht aufgerufen. Gibt es einen besseren Weg, um das zu implementieren, was ich versuche?Vererbung in C++

#ifndef ofxBASE_SND_OBJ 
#define ofxBASE_SND_OBJ 

#include "ofConstants.h" 

class ofxBaseSndObj { 

public: 

    virtual string getType(){} 

    string key; 

}; 

#endif 

Hier ist meine Summen Klasse

#ifndef OFXSO_BUZZ 
#define OFXSO_BUZZ 

#include "ofxBaseSndObj.h" 

class ofxSOBuzz : public ofxBaseSndObj 
{ 
public: 
    string getType(); 
}; 

#endif 

ofxSOBuzz.cpp

string ofxSOBuzz::getType() 
{ 
    string s = string("ofxSOBuzz"); 
    printf(" ********* returning string type %s", s.c_str()); // doesn't get called! 
    return s; 
} 

Dann in einer anderen Klasse ich versuche, es zu nennen diese Art und Weise:

string ofxSndObj::createFilter(ofxBaseSndObj obj) 
{ 
    string str = obj.getType(); 
    if(str.compare("ofxSOBuzz") == 0) 
    { 
     printf(" all is well "); 
    } 
} 

Bei dem Verfahren oben muss ich in der Lage sein, einen von m zu übergeben alle Arten von Objekten, die alle das ofxBaseSndObj-Objekt erweitern. Alle Vorschläge oder Hinweise würden sehr geschätzt werden. Vielen Dank!

+1

Soweit ich weiß, wird Ihre Kopfzeile (wo Sie ofxBaseSndObj definieren) nicht kompilieren, weil Sie eine Funktion mit nicht void Rückgabetyp ohne return-Anweisung haben. Wie hast du das geschafft? – Arkadiy

+0

Über Zeit, um eine Antwort als richtig zu akzeptieren !! Klicken Sie auf eines der Häkchen neben der Antwort, von der Sie denken, dass sie die Frage am besten beantwortet. –

Antwort

25

Ändern Sie diese Zeile:

string ofxSndObj::createFilter(ofxBaseSndObj obj) 

zu

string ofxSndObj::createFilter(ofxBaseSndObj& obj) 

Was Sie tun, um Wert ist vorbei (vorbei an einer Kopie).

Das heißt, Sie kopieren das Objekt in die Funktion. Da die Funktion nicht weiß, welchen Typ Sie tatsächlich übergeben, übergibt sie nur den Typ, der in der Funktionsdeklaration definiert ist, und erstellt daher eine Kopie der Basisklasse (dies ist als slicing Problem bekannt).

Die Lösung ist als Referenz zu übergeben.

Wenn Sie nicht möchten, dass die Funktion das Objekt ändert (vielleicht haben Sie deshalb den Wert übergeben, so dass das Original nicht geändert werden konnte), übergeben Sie eine const-Referenz.

class ofxBaseSndObj 
{ 
    public: 
     virtual string getType() const; 
     // If the method does not change the object mark it const 

     string key; 

}; 

string ofxSndObj::createFilter(ofxBaseSndObj const& obj) 
{ 
    // allowed to call this if getType() is a const 
    string str = obj.getType(); 

    if(str.compare("ofxSOBuzz") == 0) 
    { 
     printf(" all is well "); 
    } 
} 
10

Sie müssen die Instanz an createFilter als Zeiger (oder Verweis) an das Objekt übergeben. Sie sind , und dies bewirkt, dass der Compiler das abgeleitete Objekt, das Sie als Argument verwenden, in eine Instanz der Basisklasse kopiert. Wenn Sie dies tun, verlieren Sie die Tatsache, dass es ursprünglich ein abgeleiteter Typ war.

Wie geschrieben, sollte Ihr Code nicht wirklich kompilieren, da die Deklaration von ofxBaseSndObj :: getType nichts zurückgibt. Meinen Sie, dass dies eine abstrakte Methode ist oder geben Sie eine leere Zeichenfolge zurück?

Wenn Sie es zu einer abstrakten Methode gemacht haben, würde der Compiler sich beschweren, eine abstrakte Klasse in Ihrer Methode ofxSndObj :: createFilter zu instanziieren.

+0

Ja, du hast Recht, seltsam. Es sollte nicht kompilieren ... seltsam. Ich bin auf gcc4.2, ich frage mich, ob jemand anderes in das geraten ist? –

2

Dieses Problem wird "slicing" in C++ genannt.

2

Wenn Sie den Kopierkonstruktor und den Operator = privat machen, wird verhindert, dass dieser Fehler erneut auftritt.

Zum Beispiel:

class ofxBaseSndObj { 
public: 
    virtual string getType(){} 
    string key; 

private: 
    ofxBaseSndObj(const ofxBaseSndObj& rhs); 
    ofxBaseSndObj& operator=(const ofxBaseSndObj& rhs); 
}; 

Wenn es kein anderer Grund ist, sollten Sie C++ 's gebaut in RTTI verwenden.Sie können dann den Typeid-Operator verwenden. Sehen Sie in Ihrer Compiler-Dokumentation nach, ob diese Option aktiviert ist, wenn sie nicht standardmäßig aktiviert ist.

+0

Ja, ich werde ein Redesign durchführen, um stattdessen Zeiger und RTTI zu verwenden. Ich wollte vermeiden, durch Verweis zu gehen (lange Geschichte, die API hat eine ungerade Benutzerbasis), aber ich denke, es ist der einzige Weg, es wird funktionieren und es ist die Art und Weise, die am sinnvollsten ist. Vielen Dank! –

-1

Sie könnten dynamic_cast verwenden oder type_id

1

Andere das Aufschneiden Problem angesprochen haben. Sie fragen dann Ok, lassen Sie mich sagen, ich weiß, ich muss etwas tun, um den Basistyp zu bestimmen, aber gibt es etwas eleganter als eine Enum-Lookup, um die Art des geerbten Objekts zu bestimmen?

Abfrage und Einschalten des Objekttyps ist ein schlechtes Design, das den Punkt des OO-Ansatzes verfehlt.

Statt

string ofxSndObj::createFilter(ofxBaseSndObj& obj) 
{ 
    string str = obj.getType(); 
    if(str.compare("ofxSOBuzz") == 0) 
    { 
     // do ofxSOBuzz - specific thing 
    } 
    else if(str.compare("some other derived class") == 0) 
    { 
     // do stuff for other derived classes 
    } 
     // etc... 
} 

machen das interessante Verhalten der virtuellen Funktion:

class ofxBaseSndObj { 

public: 
    // get rid of getType() 
    virtual void HelpCreateFilter() = 0; 
}; 


string ofxSndObj::createFilter(ofxBaseSndObj& obj) 
{ 
    // Let the derived class do it's own specialized work. 
    // This function doesn't need to know what it is. 
    obj.HelpCreateFilter(); 
    // rest of filter creation 
} 

Warum ist das besser als die ursprüngliche Version? Weil ofxSndObj::createFilter nicht geändert werden muss, wenn zukünftige abgeleitete Klassen von ofxBaseSndObj dem System hinzugefügt werden. Ihre Version muss für jede neue abgeleitete Klasse erweitert werden. Wenn das unklar ist, versuchen Sie, ein wenig mehr Code zu veröffentlichen - ich kann nicht aus Ihrem Code oder Klassennamen sagen, was diese Funktionen tun sollen.

+0

Er ist weg - und nahm seine Frage mit. – fizzer