您所有问题的答案最终都是相同的:这取决于您是否需要引用语义或值语义(和一些caveats予以考虑)。
如果你需要参考语义,这是 Java 和 C# 等语言中默认使用的 UDT(用户定义数据类型)声明的内容class
关键字,那么你将不得不使用智能指针。在这种情况下,您希望多个客户端持有安全别名到一个特定的对象,其中这个词safe封装了这两个要求:
- 避免悬空引用,这样您就不会尝试访问不再存在的对象;
- 避免使用比所有对它们的引用都存活得更久的对象,这样就不会泄漏内存。
这是什么智能指针 do. 如果您需要参考语义(如果您的算法不会在需要共享所有权的情况下使引用计数的开销变得很大),那么你应该使用智能指针.
例如,当您希望同一对象成为多个集合的一部分时,您确实需要引用语义。当您更新一个集合中的对象时,您希望所有其他集合中同一对象的表示形式得到一致更新。在这种情况下,您可以在这些集合中存储指向对象的智能指针。智能指针封装了identity对象的性质而不是它的价值。
但如果你不需要创建别名,那么值语义可能是你应该依赖的。这是在 C++ 中声明具有自动存储功能(即在堆栈上)的对象时默认得到的结果。
需要考虑的一件事是 STL 集合存储values,所以如果你有一个vector<T>
, then copies of T
将存储在您的vector
。始终假设您不需要引用语义,如果您的对象很大并且复制成本很高,那么这可能会成为一种开销。
为了限制这种情况的可能性,C++11 附带了move操作,这使得当不再需要对象的旧副本时可以有效地按值传输对象。
我现在将尝试使用上述概念回答你的问题更直接。
1) 我应该按值返回此类,还是按智能指针返回此类?
这取决于您是否需要引用语义。该函数对该对象执行什么操作?该函数返回的对象是否应该由许多客户端共享?如果是这样,则通过智能指针。如果不是,是否可以定义一个有效的移动操作(几乎总是这样)?如果是这样,那么按价值。如果没有,则通过智能指针。
2) 在类内部,向量和对象应该是普通成员,还是应该再次存储为智能指针?
最有可能作为普通成员,因为向量通常在概念上是对象的一部分,因此它们的生命周期与嵌入它们的对象的生命周期绑定。在这种情况下,您很少需要引用语义,但如果需要,请使用智能指针。
3) 在向量中,我应该直接存储对象,还是再次存储指向它们的智能指针?
与第 1 点的答案相同:您需要共享这些对象吗?您应该存储这些对象的别名吗?您是否希望在引用这些对象的代码的不同部分中看到对这些对象的更改?如果是这样,则使用共享指针。如果没有,是否可以有效地复制和/或移动这些对象?如果是这样(大多数时候),请存储值。如果没有,则存储智能指针。
4) 我的 Results 类中定义的 getter 应该返回什么(即值、引用或智能指针)?
与第 2 点的答案相同:这取决于您计划如何处理返回的对象:您是否希望它们被代码的许多部分共享?如果是,则返回一个智能指针。如果它们仅由一个部分独占,则按值返回,除非移动/复制这些对象成本太高或根本不允许(不太可能)。在这种情况下,返回一个智能指针。
作为旁注,请注意,C++ 中的智能指针比 Java/C# 引用有点棘手:首先,智能指针有两种主要风格,具体取决于是否共享所有权 (shared_ptr
) or 独特的所有权 (unique_ptr
)是所期望的。其次,您需要避免循环引用shared_ptr
,这将创建对象岛,即使您正在运行的代码无法再访问它们,它们也可以使彼此保持活动状态。这就是为什么弱指针 (weak_ptr
) exist.
这些概念自然而然地引出了以下概念:责任用于管理对象的生命周期或(更一般地)管理已使用的资源。您可能想阅读有关 RAII 习惯用法的信息,例如(资源获取即初始化),以及一般的异常处理(编写异常安全代码是这些技术存在的主要原因之一)。