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

假设我有以下类(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());

最佳答案

没有!这将是糟糕的API 。是的,您可以轻松地在shared_ptr中实现它,但这仅仅是因为您不意味着应该这样做。

为什么这是一个坏主意? bar的基于纯指针的接口(interface)不保留共享指针的实例。如果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值(声明可以没有const,因为在那里没有关系):
    void bar(const AnotherClass a) { ... }
    
  • 如果bar不存储指针,则不要将其传递给指针:默认情况下传递const引用,或在必要时传递非const引用。
    void bar(const AnotherClass &a);
    void bar_modifies(AnotherClass &a);
    
  • 如果使用“无对象”(也称为“空”)调用bar是有意义的,则:
  • 如果按值传递AnotherClass是可以的,则使用std::optional:
    void bar(std::optional<AnotherClass> a);
    
  • 否则,如果AnotherClass拥有所有权,则传递unique_ptr可以正常进行,因为它可以为null。
  • 否则,传递shared_ptr可以正常工作,因为它可以为null。
  • 如果foo()创建了一个新对象(相对于返回一个已经存在的对象),则无论如何它应该返回unique_ptr,而不是shared_ptr。工厂函数应该返回唯一的指针:这是惯用的C++。否则会造成困惑,因为返回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)将共享所有权,
    它们分别提供对象的恒定 View 和可修改 View 。 shared_ptr<Foo>也可以转换为shared_ptr<const Foo>(但是反之则不行,您应该使用const_pointer_cast(谨慎)。您应该始终默认将对象作为常量访问,并且仅在有明确需要时才使用非常量类型它。

    如果某个方法没有修改任何东西,则通过让它接受对const something的引用/指针来使其自身记录该事实。

    关于c++ - 智能指针可以隐式用作指针吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58308058/

    10-17 01:26