0

我有 2 个简单的课程。基类A和派生类B。出于调试目的,复制构造函数和析构函数被重写为cout

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
-- 2
+A 0051F620
+B 0051F620
*A 0051F5EC
*A 00BD8BAC
-A 0051F5EC
-B 0051F620
-A 0051F620
-- 3
-A 00BD8BAC
-- 4 --

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

  1. B由我自己的代码创建
  2. B复制构造成Abypair
  3. 然后,最后一个实例被插入它A的位置再次复制map

顺便说一句,将pairininsert行更改为 apair<string, B>并没有解决问题,它仅将第二步更改为创建 anB而不是A,但最后一步会将其降级为 an A。在地图本身有资格销毁之前,地图中唯一保留的实例似乎是最后一个A实例。

我做错了什么,我应该如何将派生类放入地图?使用格式的地图

map<string, A*>

也许?

更新 - 解决方案由于我的解决方案没有直接在接受的答案中说明,而是以建议的形式隐藏在其评论中,这就是我最终做的事情:

map<string, shared_ptr<A>> m;

这假设您的构建环境支持shared_ptrs - 我的(C++/CLI,Visual Studio 2010)支持。

4

2 回答 2

4

这称为切片,当派生类不适合分配给它的对象时会发生这种情况。这是一个简单的例子:

B b;
A a = b; // This would only copy the A part of B.

如果您想在地图中存储不同类型的对象,则使用 map<..., A*> 是您正确推断的方式。由于对象不存储在映射中,而是存储在堆中的某个位置,因此无需复制它并且它总是适合的。

于 2012-07-16T07:25:06.103 回答
2
 map<string, A> m;

将对插入的对象进行切片A,这将成为一个,丢失所有其他信息(它不再是一个B)。

你的直觉是正确的,你需要使用指针,或者更好的是,智能指针。

此外,析构函数A需要是virtual

class A
{
    //...
    virtual ~A() { cout << "-A " << this << "\n"; }
};

B否则,通过指向 an 的指针删除实际类型的对象A会导致未定义的行为

于 2012-07-16T07:23:15.740 回答