一般性问题
现在我已经阅读了很多关于智能指针的内容,并且在许多情况下共享指针看起来像“完美”。但是我也读过关于周期性参考或类似的东西?哪个shared_ptr
不能使用?我有一个难以理解的时间,有人可以给出一个显示这个的简单例子吗?
另外我真的很想知道,weak_ptr提供的普通指针不是什么? - 由于它们没有增加引用计数,它们不能保证它们指向的内存仍然有效吗?
我的个人项目:
在一个项目中,我有两个“全局”容器(两个容器很快就会在一个类中移动),两者都充满了“对象”。但是两者都应该“指向”同一个对象。一个对象不能存在于这些容器之外,并且一个容器不应该包含它,而另一个容器不能包含它。
目前我只是使用普通指针,并且有一个createObject
& destroyObject
方法来管理内存。
这是好设计吗?我应该使用智能指针吗?
答案 0 :(得分:2)
回答你的各种问题:
循环引用是指两个不同的对象,每个对象都有一个shared_ptr到另一个对象。
例如:
struct Foo {
shared_ptr< Bar > m_bar;
};
struct Bar {
shared_ptr< Foo > m_foo;
};
void createObject()
{
shared_ptr< Foo > foo( new Foo );
shared_ptr< Bar > bar( new Bar );
foo->m_bar = bar;
bar->m_foo = foo;
//Neither of these objects will be released here
}
这可能导致两个对象都没有被释放,因为Foo将始终将引用计数保持在1以上,并且foo将不会被释放,因为bar将始终保持其引用计数高于1。 / p>
这是一种可以用weak_ptr克服的情况,因为它们不会增加引用计数。正如你所指出的那样,这不会阻止ptr被释放,但允许你在使用它之前检查对象是否仍然存在,这是标准指针无法做到的。
至于你提供的例子,你应该几乎总是使用智能指针而不是原始指针,因为它们允许对象在超出范围时自动释放,而不是你必须确保它自己完成,这可能容易出错。在您有异常的情况下尤其如此,这些异常可以轻松跳过您编写的任何发布代码。
例如,此代码可能会导致问题:
Foo* foo = createObject();
foo.doSomething();
deleteObject( foo );
如果除了foo.doSomething之外,那么永远不会调用deleteObject并且foo将不会被释放。
然而,这是安全的:
shared_ptr< Foo > foo = createObject();
foo.doSomething();
无论是否发生异常,shared_ptr都会在代码块结束时自动释放。
这里有关于指针和智能指针的相当好的讨论:Pointers, smart pointers or shared pointers?
答案 1 :(得分:1)
以下是循环引用的一个简单示例:
struct Node {
shared_ptr<Node> next;
};
int main()
{
shared_ptr<Node> n1(new Node), n2(new Node);
n1->next = n2;
n2->next = n1;
}
n1
和n2
指向对方,因此它们形成一个循环。 Vanilla shared_ptr
只能与directed acyclic graphs(DAG)一起使用。对于循环的,有weak_ptr
,它不会在循环时搞砸参考计数,但应小心使用。 DAG或树结构中的后退指针是weak_ptr
的有效用例,允许您备份结构。
关于您当前的项目:是的,尝试shared_ptr
,它可能会让您的生活更轻松。您可以使用use_count() >= 2
检查两个容器中是否存在对象;请注意>=2
,因为您可能会将包含对象的指针分发给客户端代码,这会增加引用次数。
答案 2 :(得分:0)
如果你使用shared_ptr,你可以得到一个指针圈,例如: p1 - &gt; p2 - &gt; p3 - &gt; p1,然后它们永远不会被释放。要打破圆圈,您可以使用weak_ptr,例如 p1 sp-> p2 sp-> p3 wp-&gt; p1,然后可以自动释放共享指针。
要记住的一点是,尽管智能指针可以避免记住显式删除资源,但它们不是灵丹妙药,你仍然可以获得内存“泄漏”,例如当你有一个指针圈时,在一个复杂的系统中,他们可能同样难以追踪。
答案 3 :(得分:0)
对于协调两个不同容器的具体问题,一种方法是将两个容器捆绑在一个将保持这种不变量的类中。
另一种方法是使用已经提供此保证的Boost.MultiIndex
。需要一些练习,我仍然建议使用相关方法包装访问,以便为用户提供以业务为中心的界面。