在C ++中clone()的最佳签名是什么?

时间:2008-11-03 20:32:25

标签: c++ clone smart-pointers covariant-return-types

正如Scott Myers写的那样,你可以利用C ++的类型系统中的松弛来声明clone()返回一个指向所声明的实际类型的指针:

class Base
{
    virtual Base* clone() const = 0;
};

class Derived : public Base
{
    virtual Derived* clone() const
};

编译器检测到clone()返回指向对象类型的指针,并允许Derived覆盖它以返回指向派生的指针。

希望让clone()返回一个暗示所有权语义转移的智能指针,如下所示:

class Base
{
   virtual std::auto_ptr<Base> clone() const = 0;
};

class Derived : public Base
{
    virtual std::auto_ptr<Derived> clone() const;
};

不幸的是,约定的放宽并不适用于模板化的智能指针,编译器也不允许覆盖。

所以,似乎我有两个选择:

  1. 让clone()返回一个“哑”指针,并记录客户负责处理它。
  2. 让clone()返回一个智能基指针,让客户端使用dynamic_cast将它们保存到Derived指针(如果需要的话)。
  3. 这些方法中的一种是首选吗?或者有没有办法让我吃掉我的所有权语义转移,并且我也有强大的类型安全性?

8 个答案:

答案 0 :(得分:26)

使用公共非虚拟/私有虚拟模式:

class Base {
    public:
    std::auto_ptr<Base> clone () { return doClone(); }
    private:
    virtual Base* doClone() { return new (*this); }
};
class Derived : public Base {
    public:
    std::auto_ptr<Derived> clone () { return doClone(); }
    private:
    virtual Derived* doClone() { return new (*this); }
};

答案 1 :(得分:19)

语法不太好,但是如果你将它添加到上面的代码中,它不会解决你所有的问题吗?

template <typename T>
std::auto_ptr<T> clone(T const* t)
{
    return t->clone();
}

答案 2 :(得分:7)

我认为函数语义在这种情况下是如此清晰,以至于几乎没有混淆的空间。所以我认为你可以使用covariant版本(返回一个指向真实类型的哑指针的那个),并且你的调用者会知道他们正在获得一个新的对象,其属性被转移给他们。

答案 3 :(得分:5)

这取决于您的使用案例。如果你认为你需要在你知道它的动态类型的派生对象上调用clone(请记住,clone的重点是允许在没有知道动态的情况下复制然后你应该返回一个哑指针并将其加载到调用代码中的智能指针中。如果没有,那么你只需要返回一个smart_ptr,这样你就可以随意返回它。

答案 4 :(得分:2)

Tr1::shared_ptr<>可以像原始指针一样进行转换。

我认为让clone()返回一个shared_ptr<Base>指针是一个非常干净的解决方案。您可以通过 shared_ptr<Derived> tr1::static_pointer_cast<Derived> 将指针投射到tr1::dynamic_pointer_cast<Derived>,以防无法确定克隆对象在编译时。

为了确保对象的类型是可预测的,你可以像shared这样使用shared_ptr的多态转换:

template <typename R, typename T>
inline std::tr1::shared_ptr<R> polymorphic_pointer_downcast(T &p)
{
    assert( std::tr1::dynamic_pointer_cast<R>(p) );
    return std::tr1::static_pointer_cast<R>(p);
}

断言添加的开销将在发布版本中丢弃。

答案 5 :(得分:1)

这是使用boost::intrusive_ptr代替shared_ptrauto/unique_ptr的一个原因。原始指针包含引用计数,可以在这种情况下更加无缝地使用。

答案 6 :(得分:1)

为C ++ 14更新MSalters answer

#include <memory>

class Base
{
public:
    std::unique_ptr<Base> clone() const
    {
        return do_clone();
    }
private:
    virtual std::unique_ptr<Base> do_clone() const
    {
        return std::make_unique<Base>(*this);
    }
};

class Derived : public Base
{
private:
    virtual std::unique_ptr<Base> do_clone() const override
    {
        return std::make_unique<Derived>(*this);
    }
}

答案 7 :(得分:0)

你可以有两个方法,一个虚拟clone()返回一个围绕基类型的智能指针包装器,一个非虚拟clone2()返回正确类型的智能指针。

clone2显然会以克隆的形式实现并封装演员。

这种方式可以获得编译时知道的派生最多的智能指针。它可能不是总体上派生类型最多的类型,但它使用编译器可用的所有信息。

另一个选择是创建一个接受你期望的类型的克隆的模板版本,但这会给调用者增加更多的负担。