c ++使用std :: list隐式复制* this

时间:2014-06-19 12:50:40

标签: c++ list oop reference std

对于一个项目,我有一个对象列表(在我的示例代码中,是一个Garden)。每个花园都包含一个植物,它引用了它所在的花园。这在制作单个花园时非常有效,但是当我创建一个花园对象的std ::列表时,突然创建了一个副本某处我不知道,我不知道如何解决它。对象如下:

struct Garden; //Forward declaration
struct Plant {
    Plant(Garden & myGarden) : theGarden(myGarden) { }; //Constructor
    Garden & theGarden; //reference to garden this Plant is in
};
struct Garden {
    Garden(int size) :  thePlant(*this), size(size) { }; //Constructor
    Plant thePlant; //Plant contained in this Garden
    int size;       //Size of this garden
};

到目前为止一切顺利。现在,我可以创建一个独立的花园,或者将其放入列表中。预期的行为是,如果我改变大小'变量,它随处可见,也在theGarden。但是,在列表中,只会在原始版本中进行更改。 Garden,而不是参考theGarden

int main() {
    //Works
    Garden oneGarden(1);
    std::cout << "Expected: 1 1, result: "
            << oneGarden.size << " "
            << oneGarden.thePlant.theGarden.size << std::endl;
    oneGarden.size = 2;
    std::cout << "Expected: 2 2, result: "
            << oneGarden.size << " "
            << oneGarden.thePlant.theGarden.size << std::endl;

    //Does not work!
    std::list<Garden> gardenList;
    gardenList.push_back(Garden(1));
    std::cout << "Expected: 1 1, result: "
            << gardenList.front().size << " "
            << gardenList.front().thePlant.theGarden.size << std::endl;

    gardenList.front().size = 2;
    std::cout << "Expected: 2 2, result: "
                << gardenList.front().size << " "
                << gardenList.front().thePlant.theGarden.size << std::endl;

    return 0;
}

最终输出如下:

Expected: 1 1, result: 1 1
Expected: 2 2, result: 2 2
Expected: 1 1, result: 1 1
Expected: 2 2, result: 2 1

1 个答案:

答案 0 :(得分:5)

标准容器拥有它们包含的元素。这意味着在插入时会复制每个元素。

复制Garden时,将使用默认的复制构造函数,而Plant成员也会默认复制。但是,这意味着新的Plant包含对 Garden的引用。

在这种情况下,旧的GardengardenList.push_back(Garden(1))中的临时Garden,因此它不仅不是正确的Garden,而且它是Garden并不是Plant甚至不再存在了。简而言之,您通过悬空引用(具有未定义的行为)读取大小,并且[un?]幸运地看到它背后的旧值。

你应该为Plant编写一个复制构造函数,以>的方式复制其Garden,除了新的gardenList.emplace_back(1); 应该引用新的Garden {{1}} 1}},而不是仅仅复制旧的引用。

使用新的C ++ 11功能实际上可以避免副本和源自它的整个问题:

{{1}}

现在,the Garden in the list is created in-place,并且不会执行任何副本。

但是,即使您以这种方式解决问题,也应该修复{{1}}的基础设计问题。