让我们说我正在编写一个可以在各种环境中重复使用的课程。它的构造函数接受我存储在成员变量中的用户定义类型。即使对于我知道复制便宜的对象,我也试图通过值或指针来判断参数是否更好。
// By value
class MyType
{
private:
SomeUdt udt_;
public:
MyType(SomeUdt udt) :
udt_ {udt}
{}
// ...
};
VS
// By pointer
class MyType
{
private:
std::shared_ptr<SomeUdt> udt_;
public:
MyType(std::shared_ptr<SomeUdt> udt) :
udt_ {udt}
{}
// ...
};
如果我通过(共享)指针接受它,那么我几乎肯定会强制客户端在堆上分配该对象。 (技术上他们可以在堆栈上分配并仍然向我发送一个指针,但这将使所有权和生命周期变得混乱。)
如果我按价值计算,那么我会删除他们管理实例的能力,例如客户是否希望SomeUdt成为单身人士。
哪种方法更好?
编辑:
此分析中还有其他一些有趣的观点。
首先,一位评论者提到,如果我用指针,那么我的私人数据就不是完全私有的。客户端可以访问我的成员对象。
另外,如果用户定义的类型需要是多态的并且有任何纯虚拟成员,那么我认为你别无选择,只能使用指针。
答案 0 :(得分:2)
这两种方法有不同的语义:
以SomeUdt
说“我想要SomeUdt
的值。”
在这种情况下,客户端代码可以生成它喜欢的值 - 可能来自临时对象,或自动分配或动态分配。这没关系,因为它不是重要的源对象,而是值。提供值后,客户端MyType
不会共享任何状态。
以std::shared_ptr<SomeUdt>
说“我想共享动态分配的SomeUdt
对象的所有权。”
在这种情况下,客户端代码应该动态分配SomeUdt
对象,使用shared_ptr
管理其生命周期,然后与MyType
共享该对象的所有权。提供对象后,客户端MyType
将共享某些状态。
除非你有充分的理由不这样做,否则你应该更喜欢按值传递对象。价值语义和本地状态总是好事。仅当需要共享所有权语义时,才应使用shared_ptr
。
请记住,如果SomeUdt
类型是可移动的,那么执行效率会更高:
MyType(SomeUdt udt) :
udt_ {std::move(udt)}
{}
如果我按价值计算,那么我会删除他们管理实例的能力,例如客户是否希望SomeUdt成为单身人士。
MyType
如何存储其州是自己的事业。为什么客户端代码需要关注?它应该由MyType
封装。
另外,如果用户定义的类型需要是多态的并且有任何纯虚拟成员,那么我认为你别无选择,只能使用指针。
如果你想要多态,那么你本身就不能要求对象的值,因为你不知道该值的类型是什么。在这种情况下,我建议采用SomeUdt&
或SomeUdt const&
(假设SomeUdt
是基类)。然后,这表示“我需要一个SomeUdt
对象”,并且不会就该对象的创建方式发出任何请求。
采用shared_ptr
纯粹是为了共享动态分配对象的所有权。它假设共享对象。
答案 1 :(得分:0)
除非有其他好的理由,否则更喜欢这种方法:
// By value, using move
class MyType
{
private:
SomeUdt udt_;
public:
MyType(SomeUdt udt) :
udt_ { std::move(udt) }
{}
// ...
};
答案 2 :(得分:0)
我认为如果您正在对数据进行修改操作,那么最好按值进行操作。 如果您的课程就像容器/读者一样,那么通过引用是理想的。