智能指针可以隐式用作指针吗?

时间:2019-10-09 16:00:09

标签: c++ c++11 shared-ptr

智能指针是否被视为指针?因此它们可以隐式用作指针吗?

假设我有以下课程:

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());

3 个答案:

答案 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 *),并且代码期望指针在startUsingfinishUsing之间保持有效。您的包装器将是:

    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); 是有意义的,则:

    1. 如果按值传递bar是可以的,则使用AnotherClass

      std::optional
    2. 否则,如果void bar(std::optional<AnotherClass> a); 拥有所有权,则传递AnotherClass可以很好,因为它可以为null。

    3. 否则,传递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方法,则使悬挂指针并不是那么困难,因此,使用它时要格外小心。

相关问题