智能指针可以隐式用作指针吗?
Can smart pointers be implicitly used as pointers?
智能指针算作指针吗?因此它们可以隐式用作指针吗?
假设我有以下 class:
class MyClass {
//...
std::shared_ptr<AnotherClass> foo() { /*whatever*/ };
void bar(AnotherClass* a) { /*whatever too*/ };
//...
}
那我可以MyClass
用下面的方法吗?
// m is an instance of MyClass
m.bar(m.foo());
不,它们不能互换使用。你会在你的例子中得到一个编译器错误。但是你总是可以通过 shared_ptr::get()
.
获得原始指针
Smart pointers用于确保对象在不再使用(引用)时被删除。
智能指针用于管理指针的生命周期 own/share。
你可以想到一个内部有指针的包装器。所以答案是否定的。但是,您可以通过 get()
方法访问他们拥有的指针。
请注意,使用get
方法制作悬挂指针并不难,所以使用时要格外小心。
不!这将是一个 可怕的 API。是的,您可以在 shared_ptr
内轻松实现它,但仅仅因为您可以并不意味着您应该。
为什么这是个坏主意? bar
的基于普通指针的接口不保留共享指针的实例。如果 bar
恰好将原始指针存储在某处然后退出,则无法保证它所存储的指针将来不会变得悬空。保证这一点的唯一方法是保留共享指针的实例,而不是原始指针(这就是 shared_ptr
的全部意义!)。
情况变得更糟:如果 foo()
returns 一个指针实例在 foo()
返回时只有一个引用(例如,如果 foo
是一个简单的新对象工厂):
AnotherClass *ptr = m.foo().get();
// The shared_ptr instance returned by foo() is destroyed at this point
m.bar(ptr); // undefined behavior: ptr is likely a dangling pointer here
这是选项;先考虑前面列出的人,然后再考虑他们的继任者。
如果bar(AnotherClass *)
是一个外部的API,那么你需要以一种安全的方式包装它,即调用Original::bar
的代码应该是调用 MyWrapped::bar
,包装器应该执行任何必要的生命周期管理。假设有 startUsing(AnotherClass *)
和 finishUsing(AnotherClass *)
,并且代码期望指针在 startUsing
和 finishUsing
之间保持有效。您的包装器将是:
class WithUsing {
std::unique_ptr<AnotherClass> owner; /* or shared_ptr if the ownership is shared */
std::shared_ptr<User> user;
public:
WithUsing(std::unique_ptr<AnotherClass> owner, std::Shared_ptr<User> user) :
owner(std::move(owner)), user(std::move(user)) {
user.startUsing(owner.get());
}
void bar() const {
user.bar(owner.get());
}
~WithUsing() {
user.finishUsing(owner.get());
}
};
然后您将使用 WithUsing
作为 User
对象的句柄,任何使用都将通过该句柄完成,确保对象的存在。
如果 AnotherClass
是可复制的并且复制起来非常便宜(例如,它由一个或两个指针组成),则按值传递它:
void bar(AnotherClass)
如果bar
的实现不需要改变值,可以定义取一个const-value(声明可以没有 const
因为它在那里无关紧要):
void bar(const AnotherClass a) { ... }
如果 bar
不存储指针,则不要向其传递指针:默认传递 const 引用,或在必要时传递非 const 引用。
void bar(const AnotherClass &a);
void bar_modifies(AnotherClass &a);
如果用 "no object" (a.k.a. "null") 调用 bar
是有意义的,那么:
如果按值传递 AnotherClass
没问题,则使用 std::optional
:
void bar(std::optional<AnotherClass> a);
否则,如果 AnotherClass
获得所有权,则传递 unique_ptr
可以正常工作,因为它可以为 null。
否则,传递 shared_ptr
可以正常工作,因为它可以为空。
如果 foo()
创建一个新对象(相对于返回一个已经存在的对象),它应该返回 unique_ptr
, 不是 一个shared_ptr
。工厂函数应该返回唯一的指针:这是惯用的 C++。否则会造成混淆,因为返回 shared_ptr
是为了表达现有的共享所有权 .
std::unique_ptr<AnotherClass> foo();
如果 bar
应该获得该值的所有权,那么它应该接受一个唯一的指针 - 这是 "I'm taking over managing the lifetime of that object":
的惯用语
void bar(std::unique_ptr<const AnotherClass> a);
void bar_modifies(std::unique_ptr<AnotherClass> a);
如果 bar
应该保留共享所有权,那么它应该占用 shared_ptr
,您将立即转换从 [=23= 返回的 unique_ptr
] 共享一个:
struct MyClass {
std::unique_ptr<AnotherClass> foo();
void bar(std::shared_ptr<const AnotherClass> a);
void bar_modifies(std::shared_ptr<AnotherClass> a);
};
void test() {
MyClass m;
std::shared_ptr<AnotherClass> p{foo()};
m.bar(p);
}
shared_ptr(const Type)
和 shared_ptr(Type)
将共享所有权,
它们分别提供对象的恒定视图和可修改视图。 shared_ptr<Foo>
也可以转换为 shared_ptr<const Foo>
(但反过来不行,你会为此使用 const_pointer_cast
(谨慎)。你应该始终默认访问对象作为常量,并且只在明确需要时使用非常量类型。
如果一个方法没有修改某些东西,让它通过接受 reference/pointer 到 const something
来自我记录这个事实。
智能指针算作指针吗?因此它们可以隐式用作指针吗?
假设我有以下 class:
class MyClass {
//...
std::shared_ptr<AnotherClass> foo() { /*whatever*/ };
void bar(AnotherClass* a) { /*whatever too*/ };
//...
}
那我可以MyClass
用下面的方法吗?
// m is an instance of MyClass
m.bar(m.foo());
不,它们不能互换使用。你会在你的例子中得到一个编译器错误。但是你总是可以通过 shared_ptr::get()
.
Smart pointers用于确保对象在不再使用(引用)时被删除。
智能指针用于管理指针的生命周期 own/share。
你可以想到一个内部有指针的包装器。所以答案是否定的。但是,您可以通过 get()
方法访问他们拥有的指针。
请注意,使用get
方法制作悬挂指针并不难,所以使用时要格外小心。
不!这将是一个 可怕的 API。是的,您可以在 shared_ptr
内轻松实现它,但仅仅因为您可以并不意味着您应该。
为什么这是个坏主意? bar
的基于普通指针的接口不保留共享指针的实例。如果 bar
恰好将原始指针存储在某处然后退出,则无法保证它所存储的指针将来不会变得悬空。保证这一点的唯一方法是保留共享指针的实例,而不是原始指针(这就是 shared_ptr
的全部意义!)。
情况变得更糟:如果 foo()
returns 一个指针实例在 foo()
返回时只有一个引用(例如,如果 foo
是一个简单的新对象工厂):
AnotherClass *ptr = m.foo().get();
// The shared_ptr instance returned by foo() is destroyed at this point
m.bar(ptr); // undefined behavior: ptr is likely a dangling pointer here
这是选项;先考虑前面列出的人,然后再考虑他们的继任者。
如果
bar(AnotherClass *)
是一个外部的API,那么你需要以一种安全的方式包装它,即调用Original::bar
的代码应该是调用MyWrapped::bar
,包装器应该执行任何必要的生命周期管理。假设有startUsing(AnotherClass *)
和finishUsing(AnotherClass *)
,并且代码期望指针在startUsing
和finishUsing
之间保持有效。您的包装器将是:class WithUsing { std::unique_ptr<AnotherClass> owner; /* or shared_ptr if the ownership is shared */ std::shared_ptr<User> user; public: WithUsing(std::unique_ptr<AnotherClass> owner, std::Shared_ptr<User> user) : owner(std::move(owner)), user(std::move(user)) { user.startUsing(owner.get()); } void bar() const { user.bar(owner.get()); } ~WithUsing() { user.finishUsing(owner.get()); } };
然后您将使用
WithUsing
作为User
对象的句柄,任何使用都将通过该句柄完成,确保对象的存在。如果
AnotherClass
是可复制的并且复制起来非常便宜(例如,它由一个或两个指针组成),则按值传递它:void bar(AnotherClass)
如果
bar
的实现不需要改变值,可以定义取一个const-value(声明可以没有const
因为它在那里无关紧要):void bar(const AnotherClass a) { ... }
如果
bar
不存储指针,则不要向其传递指针:默认传递 const 引用,或在必要时传递非 const 引用。void bar(const AnotherClass &a); void bar_modifies(AnotherClass &a);
如果用 "no object" (a.k.a. "null") 调用
bar
是有意义的,那么:如果按值传递
AnotherClass
没问题,则使用std::optional
:void bar(std::optional<AnotherClass> a);
否则,如果
AnotherClass
获得所有权,则传递unique_ptr
可以正常工作,因为它可以为 null。否则,传递
shared_ptr
可以正常工作,因为它可以为空。
如果
foo()
创建一个新对象(相对于返回一个已经存在的对象),它应该返回unique_ptr
, 不是 一个shared_ptr
。工厂函数应该返回唯一的指针:这是惯用的 C++。否则会造成混淆,因为返回shared_ptr
是为了表达现有的共享所有权 .std::unique_ptr<AnotherClass> foo();
如果
的惯用语bar
应该获得该值的所有权,那么它应该接受一个唯一的指针 - 这是 "I'm taking over managing the lifetime of that object":void bar(std::unique_ptr<const AnotherClass> a); void bar_modifies(std::unique_ptr<AnotherClass> a);
如果
bar
应该保留共享所有权,那么它应该占用shared_ptr
,您将立即转换从 [=23= 返回的unique_ptr
] 共享一个:struct MyClass { std::unique_ptr<AnotherClass> foo(); void bar(std::shared_ptr<const AnotherClass> a); void bar_modifies(std::shared_ptr<AnotherClass> a); }; void test() { MyClass m; std::shared_ptr<AnotherClass> p{foo()}; m.bar(p); }
shared_ptr(const Type)
和 shared_ptr(Type)
将共享所有权,
它们分别提供对象的恒定视图和可修改视图。 shared_ptr<Foo>
也可以转换为 shared_ptr<const Foo>
(但反过来不行,你会为此使用 const_pointer_cast
(谨慎)。你应该始终默认访问对象作为常量,并且只在明确需要时使用非常量类型。
如果一个方法没有修改某些东西,让它通过接受 reference/pointer 到 const something
来自我记录这个事实。