Reputation: 19218
I have 2 simple classes. Base class A
, and a derived class B
. For debugging purposes, copy constructor and destructor are overridden to cout
stuff:
class A
{
protected:
char * c;
public:
A(char * c) : c(c) { cout << "+A " << this << "\n"; }
A(const A& other) : c(other.c) { cout << "*A " << this << "\n"; }
~A() { cout << "-A " << this << "\n"; }
};
class B : public A
{
public:
B(char * c) : A(c) { cout << "+B " << this << "\n"; }
B(const B& other) : A(other.c) { cout << "*B " << this << "\n"; }
~B() { cout << "-B " << this << "\n"; }
};
Here's how I insert
an instance of B
into the map
:
{
cout << "-- 1\n";
map<string, A> m;
cout << "-- 2\n";
m.insert(pair<string, A>( "b", B("bVal")));
cout << "-- 3\n";
}
cout << "-- 4 --\n";
The result of that:
-- 1
-- 2
+A 0051F620
+B 0051F620
*A 0051F5EC
*A 00BD8BAC
-A 0051F5EC
-B 0051F620
-A 0051F620
-- 3
-A 00BD8BAC
-- 4 --
As regards creation of instances, I read this as follows:
B
gets created by my own codeB
gets copy-constructed into an A
by pair
A
instance then gets copied once more by the map
, where it is insertedBtw changing the pair
in the insert
line to a pair<string, B>
did not do the trick, it only changed the 2nd step to create an B
instead of the A
, but the last step would downgrade it to an A
again. The only instance in the map that remains until the map itself is eligible for destruction seems to be that last A
instance.
What am I doing wrong and how am I supposed to get a derived class into a map? Use maps of the format
map<string, A*>
perhaps?
Update - Solution As my solution is not stated directly in the accepted answer, but rather buried in its comments in form of suggestions, here's what I ended up doing:
map<string, shared_ptr<A>> m;
This assumes your build environment supports shared_ptr
s - which mine (C++/CLI, Visual Studio 2010) does.
Upvotes: 0
Views: 1395
Reputation: 6822
This is called slicing and happens when a derived class does not fit into the object it has been assigned to. This is the tirivial example:
B b;
A a = b; // This would only copy the A part of B.
If you want to store different types of object in a map, using map<..., A*> is the way to go, as you correctly deduced. Since the object is not stored inside the map but rather somewhere on the heap, there is no need to copy it and it always fits.
Upvotes: 4
Reputation: 258568
map<string, A> m;
will slice the inserted object, which will become an A
, losing all other information (it is no longer a B
).
Your intuition is correct, you'll need to use pointers or, better yet, smart pointers.
Also, the destructor in A
needs to be virtual
:
class A
{
//...
virtual ~A() { cout << "-A " << this << "\n"; }
};
otherwise deleting an object of actual type B
through a pointer to an A
leads to undefined behavior.
Upvotes: 2