将派生类作为值放入STL映射中

时间:2012-07-16 07:19:59

标签: c++ stl map derived-class

我有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 Bmap { 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

关于实例的创建,我将其阅读如下:

  1. B由我自己的代码创建
  2. A pairA复制构造成map
  3. 最后pair个实例再次被插入的insert复制
  4. 顺便将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)。

2 个答案:

答案 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的对象会导致未定义的行为