智能指针是否被视为指针?因此它们可以隐式用作指针吗?
假设我有以下课程:
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());
答案 0 :(得分:4)
不,它们不能互换使用。您的示例中将出现编译器错误。但是您总是可以通过shared_ptr::get()
来获取原始指针。
答案 1 :(得分:3)
不!这将是一个糟糕的API 。是的,您可以在shared_ptr
内轻松实现它,但这仅仅是因为您不意味着应该这样做。
为什么这是一个坏主意? bar
的基于纯指针的接口未保留共享指针的实例。如果bar
碰巧将原始指针存储在某个地方然后退出,则没有任何东西可以保证它存储的指针在将来不会悬空。保证的唯一方法是保留共享指针的实例,而不是原始指针(这是shared_ptr
的全部!)。
情况变得更糟:如果foo()
返回的指针实例在返回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值(声明可以不包含{{ 1}},因为那无关紧要):
const
如果void bar(const AnotherClass a) { ... }
不存储指针,则不要将其传递给指针:默认情况下传递const引用,或在必要时传递非const引用。
bar
如果使用“无对象”(也称为“空”)调用void bar(const AnotherClass &a);
void bar_modifies(AnotherClass &a);
是有意义的,则:
如果按值传递bar
是可以的,则使用AnotherClass
:
std::optional
否则,如果void bar(std::optional<AnotherClass> a);
拥有所有权,则传递AnotherClass
可以很好,因为它可以为null。
否则,传递unique_ptr
可以很好,因为它可以为null。
如果shared_ptr
创建了一个新对象(相对于返回一个已经存在的对象),则无论如何它应该返回foo()
,不是一个{{1 }}。工厂函数应该返回唯一的指针:这是惯用的C ++。否则会造成混乱,因为返回unique_ptr
是表示现有的共享所有权。
shared_ptr
如果shared_ptr
应该拥有值的所有权,那么它应该接受一个唯一的指针-这就是“我正在接管该对象的寿命”的惯用法:
std::unique_ptr<AnotherClass> foo();
如果bar
应该保留共享所有权,那么它应该使用void bar(std::unique_ptr<const AnotherClass> a);
void bar_modifies(std::unique_ptr<AnotherClass> a);
,您将立即将从bar
返回的shared_ptr
转换为共享一个:
unique_ptr
foo()
和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>
的引用/指针来使其成为事实,从而证明这一事实。
答案 2 :(得分:2)
Smart pointers用于确保删除不再使用(引用)的对象。
可以使用智能指针来管理其拥有/共享的指针的寿命。
您可以想到其中包含指针的包装器。所以答案是否定的。但是,您可以通过get()
方法访问它们拥有的指针。
请注意,如果使用get
方法,则使悬挂指针并不是那么困难,因此,使用它时要格外小心。