//
you're reading...
C++, Programming Issues/Tips

Storing C++ Objects in a STL Vector Part 1

1. Introduction.

1.1 The STL vector is one of the most useful container classes in STL.

1.2 However, given a base class B, a class D derived from B, and a vector V of type B, it is not possible to store instances of D in V.

1.3 As will be shown later on with code, when we try to insert an instance of class D into a vector of B, a copy of the D object will be constructed as an object of class B before being stored inside the vector.

1.4 This is one drawback of vectors : it is not able to store instances of a derived class.

1.5 Well, not exactly a drawback but a natural shortcoming due to the fact that an array must allocate contiguous memory for its items and each item must be of a fixed size (the size of the declared type as indicated in the vector template parameter).

1.6 This series of articles examine this problem and explore techniques that accomplish equivalent functionality.

2. Vector Storage Mechanism.

2.1 Before going deeper, a word about the storage mechanism of STL vectors will be useful.

2.2 When it comes to the storage of items, the STL vector employs “copy semantics”.

2.3 This means that when an object is added to it (e.g. via the push_back() method), a copy of the object (to be added) is created and then stored in the vector.

2.4 Thereafter, the original object and the vector are mutually independent of each other.

2.5 This allows for the vector to own all its internally stored copies of the “inserted” objects. When the vector is destroyed, the vector is free to and will destroy all its stored objects.

3. Storing Objects of a Derived Class into a Vector.

3.1 Let’s say we have the following 2 C++ classes with a base/derived class relationship :

class Base 
{
public:
	Base(int _x)
	{
		x = _x;
	}

	virtual ~Base()
	{
		x = 0;
	}

	int x;

	void func()
	{
		std::cout << "Base func(). x == " << x << std::endl;
	}
};

class Derived : public Base
{		
public:
	Derived(int _x, int _y) :
		Base(_x)
	{
		y = _y;
	}

	virtual ~Derived()
	{
		y = 0;
	}	

	int y;

	void func()
	{
		std::cout << "Derived func(). y == " << y << std::endl;	
	}
};

3.2 Let’s say we define the following vector of Base class instances :

std::vector<Base>

3.3 Let’s say we want to use the above vector to store an instance of Base and an instance of Derived :

std::vector<Base> vecBaseObjects;

Base b(0);
Derived d(1, 2);

vecBaseObjects.push_back(b);
vecBaseObjects.push_back(d);

The code will compile successfully. But at runtime, will vecBaseObjects indeed store a Base instance and a Derived instance ?

3.4 One way to test this is to iterate through the items in the vector and then try to dynamically cast the copy of “d” into a Derived object and then call its func() method :

void DoTest()
{
	std::vector<Base>	vecBaseObjects;

	Base b(0);
	Derived d(1, 2);

	vecBaseObjects.push_back(b);
	vecBaseObjects.push_back(d);

	std::vector<Base>::iterator	theIterator;

	for (theIterator = vecBaseObjects.begin(); theIterator != vecBaseObjects.end(); theIterator++)
	{
		Base& base = *theIterator;

		base.func();

		try
		{
			Derived& derived = dynamic_cast<Derived&>(base);

			derived.func();
		}
		catch(std::bad_cast)
		{
			std::cout << "Current object is not a Derived class object." << std::endl;
		}
	}
}

If the dynamic cast failed, a std::bad_cast exception will be thrown.

3.5 When the above code is run, the object which we can identify to be the copy of “d” (its x member will be of value 1) will not be successfully cast into a Derived object. The following will be the console output :

Base func(). x == 0
Current object is not a Derived class object.
Base func(). x == 1
Current object is not a Derived class object.

3.6 This is because when the copy of “d” is stored in the vector, what is actually stored in the vector is a copy of its Base sub-object.

3.7 This can be seen in action if we add in a copy constructor to the Base class :

class Base 
{
public:
	Base(int _x)
	{
		x = _x;
	}

	Base(const Base& rhs)
	{
		x = rhs.x;
	}

	virtual ~Base()
	{
		x = 0;
	}

	int x;

	void func()
	{
		std::cout << "Base func(). x == " << x << std::endl;
	}
};

the constructor function highlighted in bold is the copy constructor.

3.8 When the “d” is inserted into the vector :

vecBaseObjects.push_back(d);

the Base class copy constructor will be invoked :

MemoryView_CopyConstructorRHS

The memory addresses of parameter “rhs” with that of “d” in function DoTest() are the same. If we examine the memory contents at this address, we see that it contains the virtual table adderss and the values of the “x” and “y” members (1 and 2 respectively).

This implies that the Base sub-object of “d” is used to construct a Base instance which will then be stored in the vector.

3.9 Hence all objects stored inside a vector will always be of the class of which the vector was declared.

4. Conclusion.

4.1 So is there a way to overcome this problem ? Is there a way to store derived class objects in a vector ?

4.2 One way to accomplish something similar to this is to use pointers to derived class instances.

4.3 In part 2, we shall explore this possibility.

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Discussion

Trackbacks/Pingbacks

  1. Pingback: Storing C++ Objects in a STL Vector Part 2 « limbioliong - December 25, 2012

  2. Pingback: Storing C++ Objects in a STL Vector Part 3. « limbioliong - January 3, 2013

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

%d bloggers like this: