智能指针 - 为什么要使用它们,以及使用哪些?

时间:2011-11-23 12:38:39

标签: c++ smart-pointers

一般性问题
现在我已经阅读了很多关于智能指针的内容,并且在许多情况下共享指针看起来像“完美”。但是我也读过关于周期性参考或类似的东西?哪个shared_ptr不能使用?我有一个难以理解的时间,有人可以给出一个显示这个的简单例子吗?

另外我真的很想知道,weak_ptr提供的普通指针不是什么? - 由于它们没有增加引用计数,它们不能保证它们指向的内存仍然有效吗?

我的个人项目:
在一个项目中,我有两个“全局”容器(两个容器很快就会在一个类中移动),两者都充满了“对象”。但是两者都应该“指向”同一个对象。一个对象不能存在于这些容器之外,并且一个容器不应该包含它,而另一个容器不能包含它。

目前我只是使用普通指针,并且有一个createObject& destroyObject方法来管理内存。

这是好设计吗?我应该使用智能指针吗?

4 个答案:

答案 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;
}

n1n2指向对方,因此它们形成一个循环。 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。需要一些练习,我仍然建议使用相关方法包装访问,以便为用户提供以业务为中心的界面。