C++ 返回标准::唯一\u ptr<;T>;从工厂函数创建纯虚拟接口的完全隐藏实现

C++ 返回标准::唯一\u ptr<;T>;从工厂函数创建纯虚拟接口的完全隐藏实现,c++,smart-pointers,c++14,C++,Smart Pointers,C++14,我正在阅读boost文档中提供的 在“”一节中,它们提供了一个很好的习惯用法,将实现完全隐藏在纯虚拟接口后面。例如: // Foo.hpp #include <memory> class Foo { public: virtual void Execute() const = 0; protected: ~Foo() = default; }; std::shared_ptr<const Foo> MakeFoo(); 我的问题是: 你能解释一下为什么

我正在阅读boost文档中提供的

在“”一节中,它们提供了一个很好的习惯用法,将实现完全隐藏在纯虚拟接口后面。例如:

// Foo.hpp

#include <memory>

class Foo {
 public:
  virtual void Execute() const = 0;
 protected:
  ~Foo() = default;
};

std::shared_ptr<const Foo> MakeFoo();
我的问题是:

  • 你能解释一下为什么我需要公开
    ~Foo()=default
  • 仅删除受保护的
    是否有危险
  • 像单身汉一样的想法值得吗
  • 这个问题与智能指针中删除程序的工作方式有关

    在中,删除器是动态的。当你有
    std::make_shared()
    ,该对象中的删除程序将直接调用
    ~FooImpl()

    使用删除表达式或在构造过程中提供给shared_ptr的自定义删除程序销毁对象

    创建删除器时,该删除器将被复制到
    共享\u ptr

    在中,删除器是类型的一部分。它是:

    模板<
    T类,
    类Deleter=std::default\u delete
    >类唯一性\u ptr;
    
    因此,当您拥有
    unique\u ptr
    时,将直接调用
    ~Foo()
    ,这是不可能的,因为
    ~Foo()
    保护。这就是为什么当您将
    Foo()
    公开时,它会起作用。工作,如编译。您还必须使其
    虚拟化
    ——否则,只需破坏
    FooImpl
    Foo
    部分,就会产生未定义的行为

  • 这并不危险。除非您忘记将析构函数设置为虚拟,否则它会重复,导致未定义的行为

  • 这不是真正的单身汉。至于它是否值得?主要基于观点


  • 根据Barry的回答,另一种公开的方法是定义您自己的deleter,该deleter可以访问您的类“
    ~Foo()
    方法”

    示例(使用VS2013进行了尝试):

    模板
    类删除器
    {
    公众:
    void运算符()(T*a)
    {
    //显式调用a上的析构函数。
    a->~a();
    }
    };
    甲级{
    好友类deleter;//授予对deleter的访问权。
    受保护的:
    ~A(){
    //析构函数。
    }
    };
    std::unique_ptr MakeA()
    {
    返回std::unique_ptr(新的A());
    }
    
    每个
    shared\u ptr
    存储4件东西:指针、强引用计数、弱引用计数和删除器

    deleter获取从中构造
    共享\u ptr
    的类型,并删除该类型,而不是公开的类型。如果将其强制转换为基本的
    共享\u ptr
    ,则派生的删除程序仍会存储

    unique\u ptr
    默认情况下不存储这种有状态的删除器

    这背后的设计原因是
    shared_ptr
    已经在管理额外的资源:添加deleter是便宜的,因为您已经在管理引用计数

    对于
    unique\u ptr
    ,如果没有状态删除器,其开销基本上与原始指针相同。默认情况下添加一个有状态的deleter会使
    惟一性\u ptr
    的开销显著增加

    虽然它们都是智能指针,
    unique\u ptr
    实际上是最小的,而
    shared\u ptr
    则要复杂得多

    您可以通过在
    unique\u ptr
    中附加一个有状态的deleter来解决这个问题

    struct stateful_delete {
      void const* ptr = nullptr;
      void(*f)(void const*) = nullptr;
      template<class T>
      stateful_delete(T const* t):
        ptr(t),
        f([](void const* ptr){
          delete static_cast<T const*>(ptr);
        })
      {}
      template<class T>
      void operator()(T*)const{
        if (f) f(ptr);
      }
    };
    
    template<class T>
    using unique_ptr_2 = std::unique_ptr<T, stateful_delete>;
    
    template<class T>
    unique_ptr_2<T> unique_wrap_2(T* t) {
      return {t, t};
    }
    template<class T, class...Args>
    unique_ptr_2<T> make_unique_2(Args&&...args) {
      return unique_wrap( new T(std::forward<Args>(args)...) );
    }
    
    struct stateful\u delete{
    void const*ptr=nullptr;
    无效(*f)(无效常数*)=nullptr;
    模板
    有状态删除(T常量*T):
    ptr(t),
    f([](无效常数*ptr){
    删除静态广播(ptr);
    })
    {}
    模板
    void运算符()(T*)常量{
    if(f)f(ptr);
    }
    };
    模板
    使用unique_ptr_2=std::unique_ptr;
    模板
    唯一的\u ptr\u 2唯一的\u包裹\u 2(T*T){
    返回{t,t};
    }
    模板
    唯一的\u ptr\u 2使\u唯一的\u 2(Args&&…Args){
    返回唯一的包装(新的T(标准::转发(参数)…);
    }
    
    这样的
    unique\u ptr\u 2
    unique\u ptr
    的3倍大。它们不进行额外分配(不同于
    共享\u ptr
    )。它们将与非虚拟受保护的
    ~Foo
    和公共
    ~FooImpl
    一起使用


    如果我们使用统一分配的
    make_shared
    技术,并将
    ptr
    f
    的等价物存储在堆上,则可以将
    unique_ptr_2
    的大小减少到2个指针。我不确定这种复杂性是否值得节省。

    这在OP的情况下不起作用,因为唯一指针实际上包含一个指向派生的指针,上面的命令将删除基。当然,我错过了非虚拟基析构函数。
    std::unique_ptr<const Foo> MakeUniqueFoo() {
        return std::make_unique<const FooImp>();
    }
    
    template<
        class T,
        class Deleter = std::default_delete<T>
    > class unique_ptr;
    
    template <typename T>
    class deleter
    {
    public:
        void operator()(T* a)
        {
            // Explicitly call the destructor on a.
            a->~A();
        }
    };
    
    class A {
        friend class deleter<A>; // Grant access to the deleter.
    protected:
        ~A() {
            // Destructor.
        }
    };
    
    std::unique_ptr<A, deleter<A>> MakeA()
    {
        return std::unique_ptr<A, deleter<A>>(new A());
    }
    
    struct stateful_delete {
      void const* ptr = nullptr;
      void(*f)(void const*) = nullptr;
      template<class T>
      stateful_delete(T const* t):
        ptr(t),
        f([](void const* ptr){
          delete static_cast<T const*>(ptr);
        })
      {}
      template<class T>
      void operator()(T*)const{
        if (f) f(ptr);
      }
    };
    
    template<class T>
    using unique_ptr_2 = std::unique_ptr<T, stateful_delete>;
    
    template<class T>
    unique_ptr_2<T> unique_wrap_2(T* t) {
      return {t, t};
    }
    template<class T, class...Args>
    unique_ptr_2<T> make_unique_2(Args&&...args) {
      return unique_wrap( new T(std::forward<Args>(args)...) );
    }