我有2个简单的课程。基类A
和派生类B
。出于调试目的,复制构造函数和析构函数被重写为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"; }
};
以下是insert
B
中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";
的实例{/ 1>}
-- 1
-- 2
+A 0051F620
+B 0051F620
*A 0051F5EC
*A 00BD8BAC
-A 0051F5EC
-B 0051F620
-A 0051F620
-- 3
-A 00BD8BAC
-- 4 --
结果:
B
关于实例的创建,我将其阅读如下:
B
由我自己的代码创建A
pair
将A
复制构造成map
pair
个实例再次被插入的insert
复制顺便将pair<string, B>
行中的B
更改为A
没有做到这一点,它只改变了第2步创建A
而不是{ {1}},但最后一步将再次降级到A
。地图中唯一一个在地图本身符合销毁条件之前保留的实例似乎是最后一个map<string, A*>
实例。
我做错了什么以及如何将派生类放入地图?使用格式
的地图map<string, shared_ptr<A>> m;
也许?
更新 - 解决方案由于我的解决方案没有在接受的答案中直接说明,而是以建议的形式隐藏在其评论中,这就是我最终做的事情:
shared_ptr
这假设您的构建环境支持{{1}} - 我的(C ++ / CLI,Visual Studio 2010)。
答案 0 :(得分:4)
这称为slicing,当派生类不适合已分配给它的对象时发生。这是一个重要的例子:
B b;
A a = b; // This would only copy the A part of B.
如果您想在地图中存储不同类型的对象,请使用map&lt; ...,A *&gt;正如你所正确推断的那样,是要走的路。由于对象不是存储在地图中,而是存储在堆上的某个位置,因此无需复制它,它始终适合。
答案 1 :(得分:2)
map<string, A> m;
将切片插入的对象,该对象将成为A
,丢失所有其他信息(不再是B
)。
你的直觉是正确的,你需要使用指针,或者更好的是,智能指针。
此外,A
中的析构函数必须为virtual
:
class A
{
//...
virtual ~A() { cout << "-A " << this << "\n"; }
};
否则通过指向B
的指针删除实际类型A
的对象会导致未定义的行为。