2012-04-14 19 views
6

Referenzcode:C++ Vektor mit Vererbung

#include <vector> 
#include <iostream> 

class Func { 
public: 
    virtual void call() { 
     std::cout<< "Func -> call()" << std::endl; 
    } 
}; 

class Foo : public Func { 
public: 
    void call() { 
     std::cout<< "Foo -> call()" << std::endl; 
    } 
}; 

class Bar : public Func { 
public: 
    void call() { 
     std::cout<< "Bar -> call()" << std::endl; 
    } 
}; 

int main(int argc, char** argv) { 
    std::vector<Func> functors; 

    functors.push_back(Func()); 
    functors.push_back(Foo()); 
    functors.push_back(Bar()); 

    std::vector<Func>::iterator iter; 
    for (iter = functors.begin(); iter != functors.end(); ++iter) 
     (*iter).call(); 
} 

Wenn Sie sich diesen Code ausführen, erzeugt es die folgende Ausgabe auf meinem Computer:

$ ./test 
Func -> call() 
Func -> call() 
Func -> call() 

Wäre es eine Möglichkeit, dass die richtige virtuelle Funktion, um sicherzustellen, heißt in diesem Fall? Ich bin neu in C++, aber meine beste Vermutung ist, dass hier:

(*iter).call(); 

Es ist zu einem Func Objekt geworfen wird. Ist das richtig?

+0

möglich duplizieren von [Speichern Sie zwei Klassen mit derselben Basisklasse in einem std :: vector] (http://stackoverflow.com/questions/8777724/store-two-classes-with-the-same-base-class -in-stdvector) –

+0

ja, es ist jedoch, ich konnte diesen Beitrag nicht finden, bevor ich diesen erstellt habe, sorry, mein Such-fu muss nicht so gut sein wie dein sir –

+0

Es gibt Objekt-Slicing passiert in Ihrem Fall, So der abgeleitete Teil wird abgeblockt. Sie sollten Zeiger der Basisklasse für die Speicherung heterogener Objekte verwenden. – Naveen

Antwort

6

Sie sollten shared_ptr oder unique_ptr verwenden, um die Elemente einer Sammlung von polymorphen Typen zu speichern.

Während Ihr Code jetzt geschrieben wird, werden die Instanzen Foo und Bar in eine Instanz vom Typ Func eingebunden (copy constructed), um in den Vektor zu passen. (Der Grund dafür ist, dass der Vektor seine Elemente sofort mit einem festen Wert für die Performance speichert, jedoch sind die polymorphen Unterklassen zur Kompilierungszeit beliebig groß und können daher nur die Basisklasse speichern.)

Dies ist besser :

int main(int argc, char** argv) { 
    vector<shared_ptr<Func>> functors; 

    functors.push_back(make_shared<Func>()); 
    functors.push_back(make_shared<Foo>()); 
    functors.push_back(make_shared<Bar>()); 

    for (auto functor : functors) 
     functor->call(); 
} 

in dem über einer Referenzzählung Zeiger wird verwendet, um die Unterklassen von hetrogeneous Func im Vektor implizit zu teilen. (Diese Indirektion ermöglicht die Speicherung von beliebig großen Unterklassen von Func nach Adressindirection.)

Sie können auch std :: function und std :: bind betrachten, anstatt Ihren eigenen Funktortyp zu starten.

Eine andere Sache zu betrachten wäre perfekte Weiterleitung und Varadic Vorlagen.

Update: Für alte Compiler:

int main(int argc, char** argv) { 
    vector<std::tr1::shared_ptr<Func> > functors; 

    functors.push_back(std::tr1::make_shared<Func>()); 
    functors.push_back(std::tr1::make_shared<Foo>()); 
    functors.push_back(std::tr1::make_shared<Bar>()); 

    for (size_t i = 0; i < functors.size(); ++i) 
     functors[i]->call(); 
} 
+0

+1 für den Vorschlag, std :: function und bind zu betrachten – lurscher

+0

Ich kann nicht scheinen, diesen Code zu kompilieren, ist dieser Boost-Code oder Std C++? meine Compiler: 'i686-Apfel-darwin11-llvm-g ++ - 4.2 (GCC) 4.2.1 (Basierend auf Apple Inc. bauen 5658) (LLVM bauen 2336.9.00) Apple-Klirren Version 3.1 (Tags/Apfel /clang-318.0.58) (basierend auf LLVM 3.1svn) ' –

+0

@RonEliott: Sorry der Code oben verwendet den aktuellen C++ Standard (2011). Ich werde eine Version mit altem Standard schreiben, halt dran. –

1

Ihre std :: vector speichert Func Objekte - dies bedeutet, dass, wenn Sie

rufen
functors.push_back(Foo()); 
functors.push_back(Bar()); 

Sie erstellen Foo und Bar Objekte , dann "schneiden" diese Objekte, wie sie in Func Objekte kopiert werden.

Wenn Sie Foo verwenden mögen und polymorph, dann ist ein typisches Muster einen Vektor von einigem Zeigertyp (nicht „raw“ Zeiger obwohl Vorzugsweise) zu speichern Bar wäre, zum Beispiel

std::vector< std::unique_ptr<Func> >

std::vector< std::shared_ptr<Func> >

Oder, wenn Sie wirklich ..(Aber nur, wenn Sie einen älteren Compiler verwenden, die nicht über shared_ptr oder unique_ptr)

std::vector< Func* >

2

der Vektor hält nur den Typ Func nach Wert, was bedeutet, dass alle Ihre Schläfen Foo und Bar werden in Scheiben geschnitten und an der Basis gegossenen Func Typ

Sie so etwas wie std::vector< Func* > ändern müssen und dynamisch zuweisen für polymorphe Versand der abgeleiteten Klassen-Instanzen, um

Wenn yo arbeiten u sind ganz sicher, dass Sie nicht diesen Vektor zu anderen Funktionen nach dieser Funktion zurückkehrt, als Optimierungs passieren Sie die Instanzen in dem Stapel zuweisen mögen:

std::vector< Func* > v; 
Bar b; 
Foo f; 
v.push_back(&b); 
v.push_back(&f); 
+0

Das funktioniert, ja, ich kann sicher sein, dass dieser Vektor nicht weitergegeben wird, also habe ich das geplant –

1

In C++ Polymorphismus funktioniert nur mit Zeigern und Referenzen , während der Vektor Instanzen von Objekten direkt speichert. Wenn Sie push_back aufrufen, wird der Kopierkonstruktor Func aufgerufen, der das Func Objekt erstellt, das in dem Vektor gespeichert wird.

Dies wird Objekt-Slicing genannt, Sie können mehr darüber mit einer schnellen Suche in StackOverflow erfahren.

Die Lösung wäre, Zeiger (oder noch besser Smart Zeiger) auf Ihre Objekte zu speichern, die an anderer Stelle (wahrscheinlich auf dem Heap) zugewiesen werden sollten.

0

Ihr Problem ist, dass Sie einen Vektor von Func haben, aber Methoden polymorph nur durch Referenzen oder Zeiger aufgerufen werden.

1

Im Allgemeinen können Instanzen von Unterklassen größer als die ihrer Oberklasse sein. Daher sollten Sie nicht erwarten, dass die Unterklassen in den Vektor-Slot passen.

Und push_back wird wahrscheinlich intern eine Kopie Konstruktor aufrufen (der Func Klasse, da man vector<Func> haben), so dass die internen Vektorschlitze sind in der Tat Func nicht von einigen anderen Klassen.