2016-03-29 8 views
1

Ich verwende eine abgeleitete Klasse und einen Vektor von Zeigern auf Objekte aus dieser Klasse. Ich habe einige Probleme mit der Implementierung von Kopierkonstruktoren usw., auch nachdem ich viel darüber gelesen habe. Ich benutze C++ 11.Vektor von Objektzeigern und Objekten, die den Gültigkeitsbereich verlassen

Ich habe eine Schleife, wo neue Objekte der abgeleiteten Klasse erstellt werden, und ich möchte dann diese in den Vektor setzen. Da es sich jedoch um einen Vektor von Zeigern handelt, muss ich stattdessen die Adresse des erstellten Objekts angeben.

Ich glaube, dass meine Probleme von dieser Tatsache herrühren, zusammen mit Objekten, die außerhalb des Geltungsbereichs liegen und ich nicht in der Lage bin, Kopierkonstruktoren/Kopierzuweisungskonstruktoren usw. in zufriedenstellender Weise zu implementieren.

Ich habe ein minimales Beispiel gemacht, das meiner Meinung nach mein Problem veranschaulicht. Lassen Sie uns die Ersteinrichtung sagen ist wie folgt (ich weiß es nicht viel Sinn macht, zum Beispiel mit dem Zeiger *n, aber ich denke, das die Probleme mit meinem eigentlichen Code zeigt):

using namespace std; 
#include <iostream> 
#include <vector> 

class Base { 

    protected: 
    int* n; 

    public: 
    void display(string name="") {cout << name << "n = " << *n << endl;} 

    Base() {} 
    Base(int n_) { 
     *n=n_; 
     cout << "Initialised to " << *n << endl; 
    } 
}; 

class Derived : public Base { 

    public: 

    Derived() : Base() {} 
    Derived(int n_) : Base(n_) {} 
}; 

int main(int argc, const char* argv[]) { 

    vector<Base*> list; 
    for(int i=0;i<5;i++) { 
     Derived tmp(i); 
     tmp.display("Tmp: "); 
     list.push_back(&tmp); 
    } 

    cout << endl; 

    for(int i=0;i<list.size();i++) { 
     cout << "Address of " << i << ": " << list[i] << endl; 
     list[i]->display("Element "+to_string(i)+" : "); 
    } 

} 

Der Ausgang dieses ist :

Initialised to 0 
Tmp: n = 0 
Initialised to 1 
Tmp: n = 1 
Initialised to 2 
Tmp: n = 2 
Initialised to 3 
Tmp: n = 3 
Initialised to 4 
Tmp: n = 4 

Address of 0: 0x7fff3a1df2d0 
Element 0 : n = 0 
Address of 1: 0x7fff3a1df2d0 
Element 1 : n = 0 
Address of 2: 0x7fff3a1df2d0 
Element 2 : n = 0 
Address of 3: 0x7fff3a1df2d0 
Element 3 : n = 0 
Address of 4: 0x7fff3a1df2d0 
Element 4 : n = 0 

So nach der Schleife alle Elemente in der Liste auf die gleiche Adresse, wobei n = 0 (wahrscheinlich nicht definiertes Verhalten von tmp außerhalb des Gültigkeitsbereichs gehen).

So und nicht nur die Adresse tmp in list Putting Ich denke, ich muss irgendwie Instanzen Derived machen, die die Schleife überleben, während immer noch nur Adressen in list haben.

Wie erwähnt habe ich versucht, dies mit verschiedenen speziellen Member-Funktionen ohne Glück zu tun.

Beachten Sie, dass die Implementierung eines Vektors der Objekte möglicherweise einfacher zu sein scheint, aber das führt zu anderen Problemen in meinem tatsächlichen Code. Wenn ich das nicht funktioniere, werde ich es stattdessen versuchen.

+3

Die Dereferenzierung eines nicht initialisierten Zeigers ist ein undefiniertes Verhalten: Sie müssen etwas Speicher zuweisen, damit 'n' auf den Punkt zeigt, bevor Sie' * n = n_' in 'B's Konstruktor ausführen. Für 'list.push_back' (&tmp); 'haben Sie tatsächlich (automatischen) Speicher zugewiesen, aber wie Sie anhand des Werts der Zeiger sehen können, ist der Speicher immer derselbe: Er wird wiederverwendet, da der Bereich von' tmp' nur auf den Ende der Schleife – BeyelerStudios

+0

Was ist der Zweck von 'Base :: n'? Warum ist es ein Zeiger? –

+0

@BeyelerStudios: Meinst du, wie z. B. 'n = 0' vor' * n = n_' hinzufügen? @Joachim Pileborg: Wie im OP erwähnt, weiß ich, dass dies keinen Sinn ergibt, es ist eine sehr einfache Beispielversion eines großen Programms – jorgen

Antwort

4

Es hat nichts mit Kopieren oder Kopieren-Konstruktoren zu tun, es ist einfach nur, weil die Objekte den Rahmen verlassen und zerstört werden, während Sie immer noch einen Zeiger auf diese (jetzt zerstörten) Objekte haben. Dies führt zu undefined Verhalten, wenn Sie versuchen, diese Zeiger dereferenzieren.

Stattdessen müssen Sie diese Objekte dynamisch zuweisen, z. new. Sie können diese Zeiger mit C++11 smart pointers umbrechen.

+0

Zusammen mit @BeyelerStudios Kommentar funktionierte das. – jorgen

+1

Sie erhalten wahrscheinlich einen segfault wegen der Zeile "* n = n_", nicht die "neue" –

2

Sie fügen Ihrem Vektor Zeiger auf Objekte auf dem Stapel hinzu. Wenn der aktuelle Bereich endet, werden diese Objekte zerstört, aber der Zeiger ist immer noch da.

Sie müssen stattdessen neue Objekte erstellen.

int main(int argc, const char* argv[]) { 
    vector<Base*> list; 
    for(int i=0;i<5;i++) { 
     auto tmp = new Derived{i}; 
     tmp->display("Tmp: "); 
     list.push_back(tmp); 
    } 
    // ... 
    } 
} 

Jetzt müssen Sie noch sicherstellen, dass Sie die Objekte nach Bedarf freigeben. Wann immer möglich, am liebsten zurückgreifen zu unique_ptr<> oder shared_ptr<>:

int main(int argc, const char* argv[]) { 
    vector<unique_ptr<Base>> list; 
    for(int i=0;i<5;i++) { 
     auto tmp = make_unique<Derived>(i); 
     tmp->display("Tmp: "); 
     // this should move the unique_ptr and therefore transfer 
     // its ownership to the vector. 
     list.emplace_back(tmp); 
    } 
    // ... 
    } 
} 

Jetzt werden die Objekte zerstört werden, entweder, wenn sie aus dem Vektor entfernt, oder wenn der Vektor zerstört wird. Mit shared_ptr, das möglicherweise verzögert wird, bis kein Teil Ihres Programms shared_ptr<> an dasselbe Objekt hält.

Verwandte Themen