25

正如 Scott Myers 所写,您可以利用 C++ 类型系统中的放松来声明 clone() 以返回指向正在声明的实际类型的指针:

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

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

编译器检测到 clone() 返回一个指向对象类型的指针,并允许 Derived 覆盖它以返回一个指向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 将它们保存到派生指针。

这些方法之一是首选吗?或者有没有办法让我吃掉我的所有权语义转移并拥有我强大的类型安全性?

4

8 回答 8

29

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

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); }
};
于 2008-11-04T08:48:31.420 回答
19

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

template <typename T>
std::auto_ptr<T> clone(T const* t)
{
    return t->clone();
}
于 2008-11-03T21:39:06.153 回答
7

我认为在这种情况下函数语义非常清晰,几乎没有混淆的空间。所以我认为你可以放心地使用协变版本(返回一个指向真实类型的哑指针),你的调用者会知道他们正在获得一个新对象,其属性已转移给他们。

于 2008-11-03T21:28:33.103 回答
5

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

于 2008-11-03T21:02:57.867 回答
2

Tr1::shared_ptr<>可以像原始指针一样投射。

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

为了确保对象的类型是可预测的,您可以对 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);
}

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

于 2008-11-03T22:41:42.907 回答
1

这是使用boost::intrusive_ptr而不是shared_ptror的原因之一auto/unique_ptr。原始指针包含引用计数,可以在这种情况下更无缝地使用。

于 2008-11-03T21:41:22.890 回答
1

更新C++14 的MSalters 答案:

#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);
    }
}
于 2016-08-01T22:53:30.757 回答
0

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

clone2 显然会根据克隆和封装演员来实现。

这样可以得到你在编译时知道的最衍生的智能指针。它可能不是总体上派生最多的类型,但它使用了编译器可用的所有信息。

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

于 2008-11-03T21:22:52.613 回答