在C ++ 11中,有两种类型的"托管"引入了指针类型 - shared_ptr和unique_ptr。现在让我们假设我们有一组支持clone()方法的类,例如foo-> clone()将返回foo对象的副本。如果您的目标是从clone()方法返回托管指针,那么如何允许接口用户选择他想要返回哪种指针?
作为一个子问题,您是否宁愿从clone()方法返回一个原始指针,让用户自己构造shared_ptr或unique_ptr?如果没有,为什么?
答案 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()的调用本身就是一个右值。