jorgen
jorgen

Reputation: 3593

Vector of object pointers and objects going out of scope

I am using a derived class and a vector of pointers to objects from said class. I'm having some problems implementing copy constructors etc, even after reading a lot about it. I'm using c++11.

I have a loop where new objects of the derived class are created, and I then want to put these into the vector. But since it is a vector of pointers I must put the address of the created object there instead.

I believe my problems stem from this fact, together with objects going out of scope etc and my not being able to implement copy constructors / copy assignment constructors etc in a satisfactory way.

I've made a minimal example that I think illustrates my problem. Let's say the initial setup is as follows (I know it doesn't make much sense, e.g. with the pointer *n, but I think this shows the issues with my actual code):

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)+" : ");
    }

}

The output of this is:

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 after the loop all the elements in the list point to the same address, where n=0 (probably undefined behaviour from tmp going out of scope).

So rather than just putting the address of tmp into list I'm thinking I somehow have to make instances of Derived that survive the loop while still just having addresses in list.

As mentioned I've tried doing this using various special member functions with no luck.

Note that it might seem implementing a vector of the objects themselves might be easier, but that leads to other problems in my actual code. If I can't get this working I'll try that instead though.

Upvotes: 1

Views: 2667

Answers (2)

cdonat
cdonat

Reputation: 2822

You add pointers to objects on the stack to your vector. When the current scope ends, these objects will be destroyed, but the pointer is still there.

You'll have to create new objects instead.

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);
    }
    // ...
    }
}

Now you still have to make sure, you free the objects as needed. Whenever possible prefer to resort to unique_ptr<> or 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);
    }
    // ...
    }
}

Now the objects will be destroyed, either when they are removed from the vector, or when the vector is destroyed. With shared_ptr that might be delayed, untill no part of your programm holds any shared_ptr<> to the same object.

Upvotes: 2

Some programmer dude
Some programmer dude

Reputation: 409176

It has nothing to do with with copying or copy-constructors, it is simply only because the objects go out of scope and gets destructed, while you still keep a pointer to these (now destructed) objects. This leads to undefined behavior when you try to dereference these pointers.

Instead you need to dynamically allocate these objects, using e.g. new. You could wrap these pointers using C++11 smart pointers.

Upvotes: 4

Related Questions