std::unique_ptr 和 std::shared_ptr 作为虚函数的参数
std::unique_ptr and std::shared_ptr as parameters for virtual functions
我正在设计基于 virtual
方法的 C++ class 接口,以便能够提供扩展点。
许多 public 方法需要 heap
分配的对象作为参数。
因为我正在使用现代 C++ 模式,所以我打算为此使用 std::unique_ptr
或 std::shared_ptr
,但我对它们都有疑问。
使用 std::unique_ptr 看起来像这样:
class IFoo {
virtual void doSomethingWithUser(std::unique_ptr<User> user) = 0;
}
强制调用者提供 std::unique_ptr
有缺点:
- 调用者不能对提供的用户进行任何操作,因为它必须被移动
- 如果任何
doSomethingWithUser
实现需要将用户存储在某个容器中,则不能从 std::shared_ptr
构造
对所有 public 方法使用 std::shared_ptr
可以解决问题,但我们必须支付额外的内存 space 加上引用计数的原子递增和递减。
有没有我可以遵循的经验法则?
如果doSomethingWithUser
不需要所有权,则不应将其转移到此方法。
的确,复制共享指针或移动唯一指针有效地转移了资源的所有权。
您的函数参数应反映您对所传递资源的假定所有权的意图。
如果您只需要观察值,并可能改变它,您应该将非拥有句柄传递给您的函数。
如果您需要使资源保持活动状态并可能将其删除,那么您应该传递一个拥有句柄,无论它是共享它的唯一所有权。
在你的情况下,函数的名称告诉我你需要 "do something with the user",而不包含它通过调用者的生命周期。所以你应该传递一个非拥有句柄。该句柄可以是 User*
或 User&
,甚至可以是 std::reference_wrapper<User>
或 boost::optional<User&>
,具体取决于您的需要。
您的界面确实应该表达一个对象应该能够做什么,并且确实强制每个函数应该采用什么参数,但是您的界面还应该表达它对其参数的所有权。
我通常更喜欢引用,因为它们确保它不能为 null 并且可以很好地与自动存储中的对象一起使用。有些人可能会争辩说他们更喜欢将原始指针作为非空非拥有句柄,但我强烈不同意这一点,因为它们强制使用 &object
语法并允许 null.
非拥有原始指针没有错。但是,在现代 C++ 中不应使用原始拥有指针。
我正在设计基于 virtual
方法的 C++ class 接口,以便能够提供扩展点。
许多 public 方法需要 heap
分配的对象作为参数。
因为我正在使用现代 C++ 模式,所以我打算为此使用 std::unique_ptr
或 std::shared_ptr
,但我对它们都有疑问。
使用 std::unique_ptr 看起来像这样:
class IFoo {
virtual void doSomethingWithUser(std::unique_ptr<User> user) = 0;
}
强制调用者提供 std::unique_ptr
有缺点:
- 调用者不能对提供的用户进行任何操作,因为它必须被移动
- 如果任何
doSomethingWithUser
实现需要将用户存储在某个容器中,则不能从std::shared_ptr
构造
对所有 public 方法使用 std::shared_ptr
可以解决问题,但我们必须支付额外的内存 space 加上引用计数的原子递增和递减。
有没有我可以遵循的经验法则?
如果doSomethingWithUser
不需要所有权,则不应将其转移到此方法。
的确,复制共享指针或移动唯一指针有效地转移了资源的所有权。
您的函数参数应反映您对所传递资源的假定所有权的意图。
如果您只需要观察值,并可能改变它,您应该将非拥有句柄传递给您的函数。
如果您需要使资源保持活动状态并可能将其删除,那么您应该传递一个拥有句柄,无论它是共享它的唯一所有权。
在你的情况下,函数的名称告诉我你需要 "do something with the user",而不包含它通过调用者的生命周期。所以你应该传递一个非拥有句柄。该句柄可以是 User*
或 User&
,甚至可以是 std::reference_wrapper<User>
或 boost::optional<User&>
,具体取决于您的需要。
您的界面确实应该表达一个对象应该能够做什么,并且确实强制每个函数应该采用什么参数,但是您的界面还应该表达它对其参数的所有权。
我通常更喜欢引用,因为它们确保它不能为 null 并且可以很好地与自动存储中的对象一起使用。有些人可能会争辩说他们更喜欢将原始指针作为非空非拥有句柄,但我强烈不同意这一点,因为它们强制使用 &object
语法并允许 null.
非拥有原始指针没有错。但是,在现代 C++ 中不应使用原始拥有指针。