最好返回C ++引用或weak_ptr?

时间:2008-10-18 18:39:49

标签: c++ boost reference

假设我有一个类,我希望用户能够引用我的一个成员。哪个更受欢迎?

class Member;

class ClassWithWeakPtr
{
private:
   boost::shared_ptr<Member> _member;
public:
   boost::weak_ptr<Member> GetMember();
};

class Member;

class ClassWithCppReference
{
private:
    Member _member;
public:
    Member& GetMember() {return _member;}
};
你怎么看?什么时候比另一个更好?

9 个答案:

答案 0 :(得分:13)

为什么不返回shared_ptr<>?那么客户可以根据需要使用返回的内容,但是如果“服务器”类消失则没有问题。

weak_ptr<>的语义没有太多意义(缓存和???)。通常当客户要求某些东西时,它希望拥有由客户自己确定的东西(并且共享所有权和完全所有权一样好)。

如果您遇到的情况是“服务器”对象可能会在不知道客户端的情况下被销毁(您可能希望use weak_ptr<>shared_ptr<>)关于您可能做的最糟糕的事情是返回对成员的引用。在这种情况下,客户端无法知道访问返回的引用是否安全。您必须返回该成员的副本或可以正确管理所返回成员的生命周期的智能指针。

请记住,如果某个表达式产生临时ClassWithCppReference(并不总是很明显),则调用GetMember()的客户端甚至无法使用返回的引用下一个声明。

答案 1 :(得分:5)

我想提出一些内容来回应关于效率等的评论(主要来自OP和Colomon)。在实际表现方面,复制内容往往无关紧要。

我编写了很多defensive copying的程序。这是Java中的一个习惯用法,因为所有对象都是通过指针传递的,所以会有很多别名,所以你可以复制进出类的任何内容来打破别名,确保你的类的客户端不会违反你的类不变量通过在事实之后修改对象。

我编写的程序在各个地方防御性地复制了整个结构,包括整个列表和地图。为了确保性能不受影响,我对该程序进行了广泛的分析。主要的瓶颈在其他地方(即便如此,我调整了其他地方,以便留下的主要瓶颈是网络)。这个防御性的复制形象没有进入该计划的热点。


ETA:我觉得有必要澄清我的信息,因为一位评论者的阅读方式与我的预期不同,很可能其他人也这样做了。我的观点并不是可以在willy-nilly周围复制东西;但是,应该始终分析其代码的性能,而不是疯狂地猜测它将如何执行。

有时,当复制整个结构仍然提供可接受的性能,同时使代码更容易编写时,那么在我看来,这是一个更好的权衡(在大多数情况下)比代码只是略快但更多复杂。

答案 2 :(得分:4)

你应该避免放弃你的内部;这是准则n。 42个“C ++编码标准”(Herb Sutter和Andrei Alexandrescu)。如果由于某些原因你必须更好地返回 const引用而不是指针,因为 constness 不会通过它传播。

的weak_ptr&LT;&GT;似乎是一个可行的解决方案,即使它的基本目的是避免shared_ptr的循环。相反,如果您返回shared_ptr&lt;&gt;你延长了这种内部的生命,这在大多数情况下是没有意义的。

当有人处理对其内部的引用时,实例消失的问题应该是线程之间正确的同步/通信。

答案 3 :(得分:2)

我认为唯一合理的答案是,这取决于会员与班级的关系,以及您希望班级用户能够做什么。 _member是否具有独立于Class对象的有意义的存在?如果没有,那么我认为使用shared_ptr没有任何意义,无论您是返回weak_ptr还是shared_ptr。基本上要么是让用户访问一个可能比赋予它意义的Class对象更长的Member对象。这可能会防止崩溃,但代价是隐藏严重的设计错误。

正如awgn所说,你应该非常小心暴露你的班级内部。但我认为肯定有一席之地。例如,我的代码中有一个表示文件对象的类,由文件头对象和文件数据对象组成。完全复制文件类中的头部接口将是愚蠢的并且违反DRY。我想,您可以强制用户获取头对象的副本,对副本进行更改,然后将外部对象复制回整个文件类。但是这引入了很多低效率,只能让你能够使头文件对象表示不同于头对象表示。如果您确定这不是您想要做的事情,那么您也可以返回对标题的非const引用 - 它更简单,更有效。

答案 4 :(得分:1)

返回一个弱指针肯定会更昂贵并且没有任何实际意义 - 你无论如何都不能长时间地拥有所有权。

答案 5 :(得分:1)

为什么要返回弱指针?我认为你使它变得更加复杂而没有任何必要的好处。

答案 6 :(得分:0)

我基本的,无知的指导原则:

我意识到后者可能不安全,如果ClassWithCppReference的实例可能会消失,而某人仍然有成员的引用。但是,我也可以看到后一个类的POD类型的参数,比如说,设备的消息,你不想一直复制成员。

答案 7 :(得分:0)

在大多数情况下,我会提供

const Member& GetMember() const;
Member& GetMember(); // ideally, only if clients can modify it without
                     // breaking any invariants of the Class

weak_ptr< Member > GetMemberWptr(); // only if there is a specific need
                                    // for holding a weak pointer.

这背后的基本原理是(我和其他许多人)调用方法GetMember()意味着Class拥有member因此返回的参考 只会在包含对象的生命周期内有效。

在我遇到的大多数(但不是全部)代码中,GetMember()方法通常用于立即对返回的成员执行操作,而不是为以后的用户存储它。毕竟,如果您需要访问该成员,您始终可以随时从包含对象请求它。

答案 8 :(得分:-1)

根据具体情况,任何一方都可以。将“实时”链接返回给成员的主要问题(如果您必须首先公开一个)是使用公开成员的任何人是您的客户端可能持有的时间比包含对象的存在时间长。如果您的客户端在其包含对象超出范围时通过引用访问该成员,您将面临“奇怪”的崩溃,错误的值和类似的乐趣。不推荐。

返回weak_ptr&lt;&gt;它的主要优点是能够与客户端通信他们正在尝试访问的对象已经消失,而引用无法做到。

我的2便士的价值将是:

  • 如果您的客户都不会使用该成员,您作为作者是唯一使用它并控制对象生命周期的人,返回引用就可以了。 Const引用会更好,但现有代码在现实世界中并不总是可行。
  • 如果有其他人访问并使用该成员,特别是如果您正在编写库,请返回weak_ptr&lt;&gt;。它可以为您节省很多麻烦,并且可以使调试变得更容易。
  • 我不会返回shared_ptr&lt;&gt;。我已经看过这种方法,并且它通常受到对weak_ptr / weak引用概念感到不舒服的人的青睐。我看到它的主要缺点是它会人为地延长另一个对象成员的生命周期,超出其包含对象的范围。这在99%的情况下在概念上是错误的,更糟糕​​的是,可以将您的编程错误(访问不再存在的东西)变成概念错误(访问不应该不再存在的东西)。