【问题标题】:What's the best signature for clone() in C++?C++ 中 clone() 的最佳签名是什么?
【发布时间】:2008-11-03 20:32:24
【问题描述】:

正如 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 将它们保存到派生指针。

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

【问题讨论】:

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


    【解决方案1】:

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

    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); }
    };
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 像这样。为了使它看起来像标准中的东西,建议将名称更改为 make_clone()(如 make_pair)。
      • 额外提示:将其设为好友,并将成员 clone() 设为私有。
      • Nitpick:我会使用函数 accept T const &amp; t 代替——这里不需要使用指针。除此之外,我真的很喜欢这种方法。
      【解决方案3】:

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

      【讨论】:

      • +1,让用户选择最适合他的内存管理策略...另外也推荐与Boost Pointer Container一起使用:)
      【解决方案4】:

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

      【讨论】:

      • 实际上,这触发了我的意思——当我需要那个特定的类时,我可以直接使用复制构造函数调用 new。
      【解决方案5】:

      Tr1::shared_ptr&lt;&gt; 可以像原始指针一样被强制转换。

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

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

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

      【讨论】:

      • 我认为强制隐式转换为 bool 是必需的:相反,它是多余的。谢谢
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        为 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);
            }
        }
        

        【讨论】:

        • 这缺少derivedObj-&gt;clone() 可以用作Derived 而没有额外的dynamic_cast 的请求功能。我将保留返回具有协变返回类型的原始指针的私有 do_clone 函数,并使用不同的返回类型恢复公共隐藏 Derived::clone
        【解决方案8】:

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

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-02-19
          • 1970-01-01
          • 1970-01-01
          • 2011-07-19
          • 2011-01-12
          • 1970-01-01
          • 2018-04-15
          • 1970-01-01
          相关资源
          最近更新 更多