为什么用它们,用哪一个

why use them, and which to use?

本文关键字:哪一个 为什么      更新时间:2023-10-16

一般问题
现在我读了很多关于智能指针的文章,在很多情况下,共享指针似乎是"完美的"。但是我也读到过循环参考之类的东西?不能使用shared_ptr的地方?我很难理解这个,谁能举个简单的例子来说明这个?

我也很想知道,weak_ptr提供了什么普通指针没有的?由于他们不增加引用计数,他们不能保证他们指向的内存仍然有效?

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

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

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

回答您的各种问题:

循环引用是指两个不同的对象都有一个指向另一个对象的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将始终使bar的引用计数大于1,而Foo不会被释放,因为bar将始终使其引用计数大于1。

这种情况可以用weak_ptr来克服,因为它们不增加引用计数。正如您所指出的,这不会阻止ptr被释放,但允许您在使用它之前检查对象是否仍然存在,这是使用标准指针无法做到的。

对于您提供的示例,您应该几乎总是使用智能指针而不是原始指针,因为它们允许对象在超出作用域时自动释放,而不是您必须确保自己完成它,这可能容易出错。在有异常的情况下尤其如此,异常可以很容易地跳过您编写的任何发布代码。

例如,下面的代码可能会导致问题:

Foo* foo = createObject();
foo.doSomething();
deleteObject( foo );

如果foo。doSomething是except,那么deleteObject永远不会被调用,foo也不会被释放。

但是,这是安全的:

shared_ptr< Foo > foo = createObject();
foo.doSomething();

shared_ptr将在代码块结束时自动释放,无论是否发生异常。

这里有一个关于指针和智能指针的很好的讨论:指针,智能指针还是共享指针?

这里有一个简单的循环引用的例子:

struct Node {
    shared_ptr<Node> next;
};
int main()
{
    shared_ptr<Node> n1(new Node), n2(new Node);
    n1->next = n2;
    n2->next = n1;
}

n1n2相互指向,形成一个循环。香草shared_ptr应该只用于有向无环图(dag)。对于循环,有weak_ptr,它不会在循环时搞砸引用计数,但应该小心使用。DAG或树结构中的反向指针是weak_ptr的有效用例,允许您在结构中进行备份。

关于你当前的项目:是的,试试shared_ptr,它可能会让你的生活更容易。你可以用use_count() >= 2来检查一个对象是否存在于两个容器中;请注意>=2,因为您可能会将指向所包含对象的指针传递给客户机代码,这会增加引用计数。

如果你使用shared_ptr,你最终会得到一圈指针,例如p1 -> p2 -> p3 -> p1,然后它们永远不会被释放。要打破这个循环,你可以使用weak_ptr,例如:P1 sp-> p2 sp-> p3 wp-> P1,然后自动释放共享指针

要记住的是,尽管智能指针使您不必记住显式地删除资源,但它们并不是灵丹妙药,您仍然可以获得内存"泄漏",例如当您有一圈指针时,并且在复杂的系统中,它们可能同样难以追踪。

对于协调两个不同容器的特定问题,一种方法是将两个容器捆绑在一个类中,以保持这种不变性。

另一个是使用Boost.MultiIndex,它已经提供了这种保证。需要一些实践,我仍然建议使用相关方法包装访问,以便为用户提供以业务为中心的界面。