Derived classes and std::vector

I want to talk about a fun corner of C++ I ran into recently. It has to do with how to handle calling member functions of a list of heterogenous objects.

My use case was a bit like the following: say we have a bunch of different functions – a * x^2, b * x, c/x – and we want to apply them one after the other to a series of x values. Which functions are included, and the order of application are user defined.

My solution was to have a base class

and three derived classes

But when I put them in a list and applied them:

I have

g++ --std=c++14 main_wrong.cpp -o test
./test
0
0
0
0

Whaaaaa? Waah! Why is it calling the dummy base class function. Isn’t the WHOLE PURPOSE of virtual functions to ensure that the function in the derived class gets called?

The core problem here is that std::vector makes a copy of the object when we use the push_back function. We’ve defined the container type as a base class and when our derived class is passed to push_back it “slices” the object down so that the subclass now looks like the base class – losing all it’s derived attributes. (I first ran into Object slicing thanks to “Cubbi”‘s answer here)

So what can we do? We could use a vector of pointers instead. But the internet seems to think this is an unsafe idea. UNSAFE!? We don’t write C++ to be SAFE!! Oh, alright. We’ll practice safe code. What can we do?

The safest route is to use std::unique_ptr:

I’m not full clear on the use of emplace_back instead of push_back: it certainly isn’t for efficiency reasons here – the just code won’t compile using push_back.

So there you have it.

Advertisements

4 thoughts on “Derived classes and std::vector”

  1. `std::unique_ptr` is not copyable, but `push_back` tries to create a copy of the given object, so it won’t compile. `emplace_back` on the other hand will forward any argument given to the constructor of the object and construct it in place.

    Note that you have a potential resource leak in your code. You are constructing an object using `new`, and then pass that to `emplace_back`. However, if `emplace_back` fails (for instance because it can’t allocate memory), your object is not cleaned up. To work around that, you can use `std::make_unique` (only available from C++14).

  2. Hi Kaushik,

    In C++ variable of some type is an object, while in Python or Java it is rather a reference. One side effect is that you cannot (easily) create arrays or array-based containers of heterogeneous objects, simply because the object size has to be known, and if you used pure virtual method in the base class, the problem would be detected at compile time. So here comes the need to use pointers or references of some kind explicitly. Python code works the same way, just creating a reference is implicit in syntax.

  3. Hi Alexey, Thanks for the tip about pure virtual functions. Yes, in Python everything is of type PyObject and everything (including integers) being a reference to PyObject is a little trippy.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s