我应该在主要不是容器上使用shared_ptr或weak_ptr吗?

时间:2015-08-03 01:36:42

标签: c++ c++11 smart-pointers

我有两个标准容器。它们都指向相同的数据结构。第一个包含所有数据,第二个包含一些相同的数据。我应该在第二个容器上使用shared_ptr还是weak_ptr

首先,当我阅读参考文献时,我想到了在第一个集合中使用unique_ptr。我的第一个集合包含所有数据,它是唯一一个“拥有”的数据。这意味着如果数据不存在,则应将其删除。但是当我尝试创建第二个集合时,我不知道该怎么做。我创建了一个唯一的指针,但现在我需要另一个指向同一元素的指针来破坏唯一性,但事实上真正的所有者并不是新的指针。所以我理解(我希望我没错),唯一性是在达到元素的方式而不是(例如)删除元素的可能性。所以,shared_ptr。我在他的第一个系列中有它们。但现在第二个出现了,我想在这里也使用shared_ptr。访问相同数据的方式可能都是,因此所有者是两个。但在我的情况下,数据总是从之前的第二个删除。如果我使用weak_ptr,则所有者的数量不会增加。在这两种情况下,当第一个集合需要时,元素将被删除。最后我使用shared_ptr,因为weak_ptr我需要lock()每行代码中的每个指针,使其可读性降低。但我应该用什么呢?

4 个答案:

答案 0 :(得分:11)

听起来您需要 if let path = NSBundle.mainBundle().pathForResource("TextFile", ofType: "txt"){ var data = String(contentsOfFile:path, encoding: NSUTF8StringEncoding, error: nil) if let content = (data){ TextView.text = content } ,因为您的数据属于一个地方。

我建议在拥有容器中使用std::shared_ptr,然后将原始指针放在第二个和后续容器中。

这是有效的,因为你永远不会删除原始指针,但它们指向的数据仍由智能指针管理,因此当你不再需要它时会被释放。

尽管有一些不好的消息,原始指针在用作非拥有访问者时非常受尊重,这些数据由某些其他实体拥有,并会在适当的时候删除它

答案 1 :(得分:5)

您还没有获得关键信息,即您是否可以保证这两个系列具有相同的生命周期。如果你能保证两个集合都有相同的生命周期,那么对于拥有所有内容的集合和另一个集合的原始指针(如@Galik建议的那样)使用unique_ptr是理想的。

如果你不能保证两个生命周期匹配,那么你是选择shared_ptr用于第一个,还是shared_ptr用于第一个,而弱选择第二个取决于你希望销毁对象的时间。听起来只有第一个系列才是真正的拥有者,所以你需要弱指针。

但是,我强烈建议您坚持使用第一种方法。避免使用shared_ptr和weak_ptr要好得多。危险在于,如果你的两个集合有不同的生命周期,第一个集合可以在第二个集合之前销毁(只是一个错位的大括号),然后当第二个集合试图访问时,它有悬空指针。你当然可以简单地小心你的变量,但保证两个独立的局部变量始终具有相同的生命周期,这很容易搞砸。

如果同时创建同一类的两个集合元素,则可以保证它们同时构造和销毁。当然,该类应该是什么样子以及哪些代码应该去哪里的确切细节取决于问题的细节。但即使是使它们成为同一结构的唯一成员(和公共)的简单(虽然奇怪)也比使用两个局部变量更好。

答案 2 :(得分:4)

你是什么意思"每一行代码"?通常的模式如下:

int col(int pos) {
    return pos == 0 ? 1 : (pos - 1) % 3;
}

int row(int pos) {
    return pos == 0 ? 2 : (pos - 1) / 3;
}

int rows(int from, int to) {
    return Math.abs(row(from) - row(to));
}

int cols(int from, int to) {
    return Math.abs(col(from) - col(to));
}

interface MoveTest {
    boolean isLegal(int rows, int cols);
}

MoveGenerator fromTest(MoveTest test) {
    return from -> IntStream.range(0, 10)
        .filter(to -> from != to)
        .filter(to -> test.isLegal(rows(from, to), cols(from, to)));
}

MoveGenerator bishopMoves = fromTest((r, c) -> r == c);
MoveGenerator knightMoves = fromTest((r, c) -> r + c == 3);
MoveGenerator rookMoves = fromTest((r, c) -> r == 0 || c == 0);
MoveGenerator queenMoves = fromTest((r, c) -> r == 0 || c == 0 || r == c); 
MoveGenerator kingMoves = fromTest((r, c) -> r <= 1 && c <= 1);

如果拥有一个拥有容器和一个引用容器,如果所有者破坏了裁判,该引用容器可以正常工作,那么if (auto p = wp.lock()) { // p is a shared_ptr; use it as often as you need within the block. } / shared_ptr就可以了。

答案 3 :(得分:2)

好问题,

我根据经验发现,使用shared_ptr / weak_ptr,甚至shared_ptr进行扩展,往往会导致某种模糊的设计。

有些人过去一直主张shared_ptr可能被视为有害的时期。因为它使所有权浮动。而且所有权应该在设计上明确。 我不确定我是否愿意接受这个建议并提倡它;但为了回答这个问题,我肯定会在这里重复一遍。

此外,可以考虑在任何地方使用shared_ptr与使用垃圾收集相同。它只是引用计数,但它最终表现相同。只有性能不太好,它已经在过去得到证明,一个好的垃圾收集引擎比所有引用计数更快。 (这肯定是因为shared_ptr中所需的CPU原子指令和障碍,但我推测。)

也许你应该考虑转移到一个真正好的垃圾收集语言/平台,例如.NET 4上的C#?

否则,如何远离直接指针方案并使用标识符,您可以创建一个管理器模式,在此管理器中将您的2个索引数据结构设为私有。客户只能通过管理员的API查看和使用标识符(intuint64_t?由您自行决定)。

我发现这种方法的问题是需要在管理器中重复操作对象的整个API。这很痛苦,不尊重DRY

否则,您是否认为您的数据结构可能不是必需的? 我的意思是,无论如何,当你存储指针时,索引数据结构往往不会那么大。它只是N*sizeof(ptr_t)而不是N*sizeof(value_t)。这突然使std::vector成为所有情况下的优秀候选人。我的意思是向量已经是几乎所有用法的最佳数据结构there are many advocates of this theory

如果您的向量只保留指针,请使用shared_ptr来帮助自己,并减轻boost::ptr_vector开销。

我希望我带来一些观点。