从克隆方法返回托管指针

时间:2014-06-03 08:55:19

标签: pointers c++11

在C ++ 11中,有两种类型的"托管"引入了指针类型 - shared_ptr和unique_ptr。现在让我们假设我们有一组支持clone()方法的类,例如foo-> clone()将返回foo对象的副本。如果您的目标是从clone()方法返回托管指针,那么如何允许接口用户选择他想要返回哪种指针?

作为一个子问题,您是否宁愿从clone()方法返回一个原始指针,让用户自己构造shared_ptr或unique_ptr?如果没有,为什么?

2 个答案:

答案 0 :(得分:3)

管理动态分配的标准智能指针始终为unique_ptr。相比之下,shared_ptr是一个非常具体的工具,具有专门的功能(例如类型擦除删除器,弱指针观察器)和更高的成本(虚拟调度,锁定原子操作),只有当你确定知道你想要它时才能使用它。公共原始指针是原则上的禁忌,因此自然clone接口如下所示:

struct Base
{
    // must have virtual destructor to destroy through base pointer
    virtual ~Base() {}

    // non-leaf classes are abstract
    virtual std::unique_ptr<Base> clone() const = 0;
};

struct Derived : Base
{
    virtual std::unique_ptr<Base> clone() const override
    {
        return std::unique_ptr<Derived>(new Derived(*this));
        // or "return std::make_unique<Derived>(*this)" in C++14
    }
};

(不幸的是,我们不能在这里使用任何类型的协变返回类型,因为模板类unique_ptr<Base>unique_ptr<Derived>是不相关的。如果您希望有一个返回派生类型的克隆函数,可以添加一个非虚拟函数,如direct_clone,返回std::unique_ptr<Derived>,并根据该函数实现虚拟clone()。)

答案 1 :(得分:0)

沿着这条线的某些东西可以让你选择返回的智能指针的种类。如果封装在mixin Clonable类模板中,可能会更好,以实现该概念的可维护性和可重用性。

#include <iostream>
#include <memory>

class Base {
public:
  virtual ~Base() {
    std::cout << "deleting Base\n";
  }

  template <template <typename ...Args> class SmartPtr>
  SmartPtr<Base> clone() {
    return SmartPtr<Base>(this->inner_clone());  
  }

  virtual void speak() const = 0;

private:
  virtual Base *inner_clone() const = 0;
};

class C: public Base {
public:
  ~C() {
    std::cout << "deleting C\n";
  }

  template <template <typename ...Args> class SmartPtr>
  SmartPtr<C> clone() {
    return SmartPtr<C>(this->inner_clone());  
  }

  void speak() const {
    std::cout << "I am C and I inherit from Base!\n";
  }

private:
  C *inner_clone() const override {
    return new C(*this);
  }
};

// End boilerplate.

int main()
{
  auto original = C{};
  // the declarations below should use auto, and are just explicitly typed to
  // show the correct return type of clone();
  std::shared_ptr<C> shared = original.clone<std::shared_ptr>();
  std::unique_ptr<C> unique = original.clone<std::unique_ptr>();

  // the declarations below show it working through conversion to a base class
  // smart pointer type
  std::shared_ptr<Base> sharedBase = original.clone<std::shared_ptr>();
  std::unique_ptr<Base> uniqueBase = original.clone<std::unique_ptr>();

  // the declarations below show it working through the base class for real
  std::shared_ptr<Base> sharedBaseFromBase = sharedBase->clone<std::shared_ptr>();
  std::unique_ptr<Base> uniqueBaseFromBase = uniqueBase->clone<std::unique_ptr>();

  shared->speak();
  unique->speak();
  sharedBase->speak();
  uniqueBase->speak();
  sharedBaseFromBase->speak();
  uniqueBaseFromBase->speak();
}

使用gcc 4.8.1编译,并且应该在支持可变参数的任何编译器中使用。

我仍然希望简单地返回一个unique_ptr并将结果移动到shared_ptr中,这将是自动的,因为对clone()的调用本身就是一个右值。