C ++ - 清除存储在两个地方的指针数组

时间:2015-10-15 14:26:44

标签: c++11 vector

在类Block的构造函数中,我将创建的实例推送到静态向量static std::vector<Block*> allBlocks,并在析构函数中删除它们。

如果我只是创建没有指针的块,即。 Block b;

但是,希望让另一个类返回std::vector<Block*> grid以供使用,它会创建它们并将它们也添加到allBlocks向量中。销毁grid向量似乎不会运行它们的析构函数。

我试过了: grid.clear() erase/remove使用pop_back

什么是存储/返回它们的更好方法,以便在grid被销毁时,包含的Blocks也将被删除。

1 个答案:

答案 0 :(得分:4)

好的,所以如果你想要一个更好的方法,可以做一些改变:

  • 没有static s!在这里根本不需要它们。
  • 如果你已经说过,你想要两个包含那些对象的容器,但是这样一来,删除一个对象会将它们从其他所有容器中删除,那就更容易出问题了。

首先,一个容器不可能从另一个容器中删除元素,除非它在某种程度上引用了它。您可以创建一个变量来保存块的所有容器,并使用 从每个容器中删除块,但是......是的。

在这种情况下,只要您记住其含义,就可以接受弱参考解决方案。

std::shared_ptr所有权和std::weak_ptr参考

创建一个std::set<shared_ptr<Block>> blocks;,然后创建两个带弱引用的容器;可能被称为allBlocksgrid或其他。那些弱引用集合可以是例如std::set<std::weak_ptr<Block>>

现在,当从gridallBlocks中删除元素时,您需要将其从blocks中删除。要对它进行查找,你需要这样的东西:

struct null_deleter {
    void operator()(void const *) const { }
};

为集查找正确创建值。然后,当迭代任何其他容器时,您需要在ptr.expired()引用上使用weak_ptr,以查看它是否先前已被删除。

该想法的警告是原始shared_ptr不是共享;该类仅用于方便weak_ptr和自动销毁。

std::unique_ptr所有权和int参考

另一种更简单的方法是使用std::unordered_map并为每个块创建一个“唯一ID”键。

std::unordered_map<unsigned, std::unique_ptr<Block>> blocks;

现在你的容器必须是std::set<unsigned>,查找看起来像是:

for (auto b : grid) {
    auto bIt = blocks.find(b);
    if (bIt != blocks.end) {
        // do things with *bIt
    } else {
        // e.g. add the b to the "delete list"
    }
}

现在您可以处理“删除列表”并从容器中删除死ID。

结束

由于使用起来可能会很繁琐,一个好主意可能是将set包装到一个容器中,该容器会在返回begin()end()之前进行清理,以便在Block中进行自定义迭代值。

类似地,这种包装结构的析构函数可以从地图中删除值,从而有效地使所有其他容器中的所有id悬空。

这当然会引发线程安全问题,因为原始blocks映射需要在整个迭代中被锁定;至少是为了修改。让cbegin()/cend()允许两个线程从同一个共享map读取,但是......在线程之间共享数据时出现的问题超出了这篇文章的范围。

中毒

我想到的另一个想法有时被称为“中毒”。在这种情况下,您不需要主容器;两个常规容器都会将shared_ptr保存到Blocks ...并且有一个扭曲。

当选择一个块进行删除时,会在其上设置一个特殊标志。它变得“中毒”,每个容器都应该在迭代之前扫描这些块。

如果每个容器确实如此,那么对Block die及其析构函数的所有引用都将正确启动。您实际上是通过它的特殊值来传达命令。

如果你不想修改Block类,那么拥有std::shared_ptr<std::optional<Block>>并且使optional无效可以正常工作,除了Block的析构函数会立即运行,而不是最后一次运行结构决定进行扫描。根据您的目标和需求,这可能会更好或更差。