views:

165

answers:

4

I need help with pointers and memory management.

I need to store different objects, all derived from the same base class, and have been using an array to do this but it is causing a segmentation fault when the array is populated with different objects.

My program works fine when the array is full of objects of the same derived type. When the array is populated with different objects it works as it is supposed to through the object stored at the first position but then when it switches to output the second object it gives me the segmentation fault. I know that this is a memory access issue but I am unclear how I'm supposed to manage a variable amount of objects dependent on user input.

thx, nmr

+4  A: 

Make sure the pointers you're pushing on the stack are dynamically allocated. The following will fail:

std::vector<Base*> objects;

void make_one(void)
{
    Derived d;

    objects.push_back(&d);
}

Because when the function ends, the class pointed to by &d will be deallocated. This is alleviated by dynamically allocating the objects:

std::vector<Base*> objects;

void make_one(void)
{
    Derived *d = new Derived;

    objects.push_back(d); // a-ok
}

Just remember to go through the vector when you're done, and call delete on them:

struct deleter
{
    template <typename T>    
    void operator()(T* pObject) const
    {
        delete pObject;
    }

}

std::for_each(objects.begin(), objects.end(), deleter());

If you can use boost, there is a pointer container library that will do this for you. Note, you cannot use auto_ptr and try to let it do it for you; auto_ptr's don't play well with containers.

Also, make sure your base classes have virtual destructors:

struct base
{
    virtual ~base(void) {} // important!
}

If they are not, calling delete on a base class will run the base constructor only, leaking any resources the derived class might of had. By making it virtual, the compiler can jump to the correct destructor.

GMan
"will run the base constructor only" ... yielding undefined behavior, IIRC.
Steve Jessop
Better yet: directly use the right container if you can >> `boost::ptr_vector` at http://www.boost.org/doc/libs/1_41_0/libs/ptr_container/doc/ptr_vector.html
Matthieu M.
To clarify, we have to allocate dynamically BECAUSE we're storing pointers, right? We could store regular objects in a vector w/o worrying about deallocation at the end of the function?
Dave
You have to dynamically allocate because the container outlives the objects it's pointing to. You don't *have* to dynamically allocate if you can guarantee the pointer will be removed from the container before the object goes out of scope. Storing dynamically allocated pointers removes the responsibility and place it on the hold of the container.
GMan
+1  A: 

I won't post a complete solution because you have identified the question as homework, but I hope I can help you out with the problem a little bit:

Arrays are designed to hold many objects of the same size. The problem with storing different objects in the array (even if they are derived from the same base class) is that the objects are likely to have different sizes.

You're definitely on the right track by thinking about pointers.

edit (in response to comments):

You would be looking at something like this:

BaseClass * array[size];
array[0] = new DerivedClass(...);
array[1] = new OtherDerivedClass(...);
...

A pitfall of this approach would be that there is no built-in deletion of the objects in the array. You would have to loop through and call delete manually:

for (int index = 0; index < size; index++) { delete array[index]; }
e.James
If I use an array of base class pointers that each point to a different derived object would that work? I think I have tried that iteration to no avail. Or would I have to store the address of each pointer to a derived object in said array of base pointers? That's a tongue twister.
nmr
An array of base class pointers should definitely work.
e.James
What would the syntax of that look like? I'm fairly confident my brain is fried at the moment and I can't work out the syntax. Would it just be: Class * Cptr; Class * cptr[size]; Cptr = new Derived( something, something ); std::cout << cptr[ 0 ] -> DerivedFunc();Something like that?
nmr
Sort of. See my response above. Also, get some sleep :) it really does all look better in the morning.
e.James
Are you not allowed to use vector? That would simplify everything immensely.
GMan
A: 

It looks very similar to the problem mentioned here: Is array of derived same as array of base? . Are you creating a array of derived objects and trying to access as if it is an array of base?

You can use array of base pointers like this, but note that it is better to use std::vector<Base*> instead of raw arrays:

class Base
{
public:
    virtual ~Base(){}
    virtual void f() {std::cout<<"Base::f()\n";}
};


class Derived1: public Base
{
public:
    ~Derived1(){}
    void f(){std::cout<<"Derived1::f()\n";}
};

class Derived2: public Base
{
public:
    ~Derived2(){}
    void f(){std::cout<<"Derived2::f()\n";}
};


void print(Base** p, int count)
{
    for(int i = 0; i < count; ++i)
    {
     (*p)->f();
     ++p;
    }
}

int main()
{
    Base b;
    Derived1 d1;
    Derived2 d2;

    Base* arr[3];
    arr[0] = &b;
    arr[1] = &d1;
    arr[2] = &d2;

    print(arr, 3);


    return 0;
};
Naveen
The inverse, I've created an array of base objects and attempting to access it as an array of derived objects of variable size. I have since realized this won't work and am trying to work out the syntax of doing an array of base class pointers. I will check out link you posted which looks helpful.
nmr
In that case, object slicing occurs (assuming you are storing it as an object in the array) and all the objects in the array are actually base-class objects. You can not cast it to a derived class object and try to use it.
Naveen
+1  A: 

A very relevant discussion here: http://stackoverflow.com/questions/1361139/c-vector-pointers-to-objects

Joy Dutta