vectors and derived classes

Pages: 12
Hi, all -

Today I finally encountered a need for using base and inherited classes, so I'm stumbling my way along. First question: if I create a vector of a base class object, can I put objects of a derived class into it?

Thanks...more questions to follow, I'm sure.
absolutely! derived classes in arrays or vectors of base classes is one of the most useful properties of polymorphism. However, the object can't just be of the type, rather it needs to be a pointer of the the base class. The concept of polymorphism is that a pointer of a base class can point to any class that derives from it. here is an example i had lying around:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <vector>
using namespace std;

class a
{
protected:
    int test;
public:
    virtual void SetTest(int arg) {test = arg;}
    int GetTest() {return test;}
};

class b : public a
{
public:
    void SetTest(int arg) {test = arg+1;}
};

class c : public a
{
public:
    void SetTest(int arg) {test = arg+2;}
};

int main()
{
    vector<a *> derivedClassHolder;
    derivedClassHolder.push_back(new b);
    derivedClassHolder.push_back(new c);
    derivedClassHolder.push_back(new a);
    derivedClassHolder.push_back(new c);
    derivedClassHolder.push_back(new b);

    for(int i = 0; i < (int)derivedClassHolder.size() ; i++)
    {
        derivedClassHolder[i]->SetTest(5);
    }

    for(int i = 0; i < (int) derivedClassHolder.size() ; i++)
    {
        cout << derivedClassHolder[i]->GetTest() << "  ";
    }
    return 0;
}


the output would be 6 7 5 7 6.
Last edited on
Putting an object of derived type into an vector of base type will compile, but the result is what's known as "object slicing": only the base subobject will be stored in the vector, losing everything that the derived class added (demo: http://ideone.com/5WmjZ )

What you need is a vector of pointers to Base. The best options are std::vector<std::unique_ptr<Base>> (demo: http://ideone.com/Z5bQk ) and boost::ptr_vector<Base> (demo: http://ideone.com/Y9nOG ), but other solutions, such as vector<shared_ptr<Base>> or even vector<Base*> may be used, with care.
Last edited on
Agreed,

vector<Base*> is the last resort. Who owns the memory pointed to in this case? Is it the vector or is it something else that
created the object? This leads to lots of headaches, and potential for memory leaks and or dangling pointers.

Much easier to use smart pointers from the start

Hmm...OK, you're making me wonder if I'm on the right track here.

Here's the deal: I have a class that contains a vector of another class. I'd like that vector to be a mixture of the base class, and a derived class.

I'm already getting like 5 classes nested into each other in this program, so I'm trying to keep things simple. Ideally, I'd create that vector in the constructor of the "containing" class (I don't know the right term for it).

If the vector contains pointers, then the containing class will have to populate those pointers, and allocate the memory for them, right?
And, mik2718: what do you mean by "smart" pointers?

Here's what my class looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class DemodNyqCell
{
private:
	vector<NyqMultBlock>		blocks;
...
DemodNyqCell::DemodNyqCell(int32_t rv)    // default constructor
{
	blocks.reserve(DEMOD_NYQ_NBR_BLOCKS);
	for (int i = 0; i <= DEMOD_NYQ_NBR_BLOCKS; ++i)
	{

		blocks.push_back(NyqMultBlock(rv));
	}
}


This is how I was doing it before I realized I needed a derived class of NyqMultBlock. Now, I have to do something else.
Total ownership by a containing class is one option. The containing class makes its own copies of the objects in the vector.
Then there is no chance of them disappearing under its nose.

You need to add a virtual clone() function to the base class of the objects you are containing

1
2
3
4
5
6
7
8
9
class derived : public base
{

  // stuff

  public:
  Base* clone() const
    { return new Derived(*this); }
};


the clone function returns a copy of the object on the heap pointed to by a Base* pointer. The containing class uses this to make its own copies of everything it contains.

That's one way of dealing with it.


Sorry if I'm being dense here, but...why do I want to make a copy of the object?

In my program, an object of class DemodNyqCell contains a vector of 8 objects of class NyqMultBlock. This was straightforward until I introduced a derived class of NyqMultBlock. (A twist to this is that I'll have 16 DemodNyqCell objects, with differing mixtures of the base and derived objects.) Originally, a vector seemed like the right approach, but if my modification makes a different data structure a better idea, I'm open to suggestion.
maybe i can help here. On my explanation, i was telling you that you could make a vector in your container class of type NyqMultBlock * and then add new instances of the base and derived objects to the vector. What mik and cubbers is telling you is ways of ensuring correct destruction of the new objects. Dynamic allocation with new always requires the use of delete at the end of the program or when you're done with the objects, and it's just hit or miss to make sure that the new instance of the objects in your vector are correctly destructed. Smart pointers are abstract objects that represent pointers to the object which do much of the book keeping and destruction of the objects at program termination. Mik's method of making a copy of the object is another way of ensuring that all the objects have centralized ownership, and at the end of the program, all of the new objects created there are destroyed along with the object that the others are copied from.

basically what he is suggesting you do is, when you add a new instance of an object to the vector, you just do something like this,

blocks.push_back(derived::clone() );

or maybe i misinterpreted mik's intentions entirely?
Last edited on
My vectors are going to be pretty much static: they'll be created to a pre-known size when the containing object is constructed, and remain that size until the program exits.

I can tell that mik knows a lot more about this than I do, but...I don't like the idea of making two copies of my vectors. I don't see the value in that (yet). If anything, I'll make two vectors: one for the base class objects, and one for the derived class objects.

Again, I'm trying to keep my data structure as simple as possible, within reason.
if it's just the concept of dynamic allocation that's confusing you don't worry, it's actually not that bad. Here take a look at this.

1
2
3
4
5
6
7
8
9
10
class b
{
      int foo;
};

int main()
{
     b test;
     return 0;
}


is the same as:

1
2
3
4
5
6
7
int main()
{
      b * test;
      test = new b;
      delete test;
      return 0;
}


just that the memory is allocated at runtime. But, if you want a derived class to be created at a pointer of the base class, there is no way to do so via the conventional method. Enter polymorphism.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class a
{
protected:
     int foo;
}

class b : public a
{
};

int main()
{
      a * meh;
      meh = new b;   
/*here you can make meh point to an object of type a or b, here it points to a new object of type b*/
      delete meh;
      return 0;
}


What i'm showing you here is basically the exact same thing as with the vector, except storing new objects of type a or b in the vector.

what mik was trying to do was not make a copy of the object, it was making a brand new instance of the object, execept based off of some premade stock object, and that would make all of those objects made off of that copy be owned by the original, so at the end of the program, when the original object is destroyed, so are all the copies of it, ensuring that there is no memory leak. you would be able to treat all those copies just as if they were different objects changing there original values and whatnot
Last edited on
thats right you would have something like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class container
{
  vector<Base*> vec;

public:
  void push_back(const Base& obj)
    { vec.push_back(obj.clone()); }
  void set(int i,const Base& obj)
    { delete vec[i];                // note: make sure destructor is virtual
       vec[i] = obj.clone(); }
  const obj& get(int i) const
    { return *vec[i]; }

};


the container manages everything about the objects: creation, destruction etc.

this method does mean you have the expense of making copies - so if you don't want that its not for you.

if the ownership between the container and outside is not clear then shared_ptr is the way to go.
I guess you could add some 'emplace' methods which avoid copies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class container
{
  vector<Base*> vec;

public:
  void push_back(const Base& obj) // looks like a usual vector<int> type thing
    { vec.push_back(obj.clone()); }
  void emplace_back(Base* objptr) // should always be used container.emplace_back(new obj); hands ownership to container
    { vec.push_back(objptr); }
  void set(int i,const Base& obj)
    { delete vec[i];                // note: make sure destructor is virtual
       vec[i] = obj.clone(); }
  const obj& get(int i) const
    { return *vec[i]; }

};


Thanks for the explanations, guys. mik: perhaps I don't understand what you mean by "copy" -- are you talking about creating a duplicate vector (one for the containing class and one for the sub-class)? If so, that doesn't appeal to me. If not, then I just need to understand your idea a little better.

And, I don't think the emplace_back() method is part of standard C++ vectors. At least there's nothing about it on the reference page.

Thanks for bearing with me, guys...I'll get this eventually.
Following logic of making the container take care of obj destruction I would believe we should add a destructor for our our container class that iterates through the vec deleting the Base ptr obj's.



Still playing with this stuff.

Are "smart pointers" part of standard C++? The code I'm writing has to be portable; in fact, I don't yet know on what platform it will ultimately be deployed.

Last edited on
Are "smart pointers" part of standard C++?

Yes, but most smart pointers (including the ones you need) were not part of the old standard described on this website's reference section (incidentally, the old standard also lacked the emplace family of functions)

The boost library makes several standard-compatible smart pointers available for legacy compilers: http://www.boost.org/doc/libs/release/libs/smart_ptr/smart_ptr.htm and so does QTCore library and nearly every other C++ library. But if you are going to use boost (which is normal, every serious C++ project uses some part of boost, these days), then you don't even need smart pointers at all: boost::ptr_vector is exactly the container you're looking for: http://www.boost.org/doc/libs/release/libs/ptr_container/doc/ptr_container.html
Last edited on
Thanks, Cubbi. I'm in a bit of a time-crunch to get this implemented; is it going to take me long to get started with boost?
Time crunch may not be the best time to learn about the C++ object ownership models and object lifetime management. If you're comfortable with raw pointers, use a vector of raw pointers to Base, vector<NyqMultBlock*>, as suggested earlier. Just make sure your program executes delete only once per each contained object.

But if I create a vector of base pointers, I can't use it to access the subclasses, can I?
Pages: 12