如何在 C++ 中克隆为派生对象

问题描述 投票:0回答:4

我在 C++ 中定义了两个类。一个是基类,一个是派生类

    class CBaseClass
    {
    …
    }

    class CDerivedClass : public CBaseClass
    {
    …
    }

并想实现一个克隆功能如下:

    CBaseClass *Clone(const CBaseClass *pObject)
    {
    }

当 CDerivedClass 的对象被传递给 Clone 时,该函数也会创建一个 CDerivedClass 对象并返回。 当 CBaseClass 的对象传递给 Clone 时,该函数也会创建一个 CBaseClass 对象并返回。

如何实现这样的功能?

c++ clone
4个回答
6
投票

虚拟克隆模式通常用于解决此类问题。经典解决方案倾向于对

clone()
方法使用协变返回类型。其他解决方案在基类和派生类之间注入工厂类型类(使用 CRTP)。甚至有一些解决方案只是使用宏来实现此功能。请参阅 C++ FAQC++ 惯用语关于此的博客。这些解决方案中的任何一种都是可行的,并且最合适的解决方案将取决于它们的使用环境和打算使用的环境。

一种经典方法,使用协变返回类型并结合更现代的 RAII 技术(

shared_ptr
等)提供了非常灵活和安全的组合。协变返回类型的优点之一是您能够在层次结构中获得与参数相同级别的克隆(即返回并不总是到基类)。

该解决方案确实需要访问

shared_ptr
和/或
unique_ptr
。如果您的编译器不支持,boost 会提供这些的替代方案。
clone_shared
clone_unique
是根据标准库中相应的
make_shared
make_unique
实用程序建模的。它们包含对参数和目标类型的类层次结构的显式类型检查。

#include <type_traits>
#include <utility>
#include <memory>

class CBaseClass {
public:
  virtual CBaseClass * clone() const {
    return new CBaseClass(*this);
  }
};

class CDerivedClass : public CBaseClass {
public:
  virtual CDerivedClass * clone() const {
    return new CDerivedClass(*this);
  }
};

class CMoreDerivedClass : public CDerivedClass {
public:
  virtual CMoreDerivedClass * clone() const {
    return new CMoreDerivedClass(*this);
  }
};

class CAnotherDerivedClass : public CBaseClass {
public:
  virtual CAnotherDerivedClass * clone() const {
    return new CAnotherDerivedClass(*this);
  }
};

// Clone factories

template <typename Class, typename T>
std::unique_ptr<Class> clone_unique(T&& source)
{
  static_assert(std::is_base_of<Class, typename std::decay<decltype(*source)>::type>::value,
    "can only clone for pointers to the target type (or base thereof)");
  return std::unique_ptr<Class>(source->clone());
}

template <typename Class, typename T>
std::shared_ptr<Class> clone_shared(T&& source)
{
  static_assert(std::is_base_of<Class, typename std::decay<decltype(*source)>::type>::value,
    "can only clone for pointers to the target type (or base thereof)");
  return std::shared_ptr<Class>(source->clone());
}

int main()
{
  std::unique_ptr<CDerivedClass> mdc(new CMoreDerivedClass()); // = std::make_unique<CMoreDerivedClass>();
  std::shared_ptr<CDerivedClass> cloned1 = clone_shared<CDerivedClass>(mdc);
  std::unique_ptr<CBaseClass> cloned2 = clone_unique<CBaseClass>(mdc);
  const std::unique_ptr<CBaseClass> cloned3 = clone_unique<CBaseClass>(mdc);
  // these all generate compiler errors
  //std::unique_ptr<CAnotherDerivedClass> cloned4 = clone_unique<CAnotherDerivedClass>(mdc);
  //std::unique_ptr<CDerivedClass> cloned5 = clone_unique<CBaseClass>(mdc);
  //auto cloned6 = clone_unique<CMoreDerivedClass>(mdc);
}

我添加了

CMoreDerivedClass
CAnotherDerivedClass
来稍微扩展层次结构,以更好地显示类型检查等。

示例代码


2
投票

这是一个简单的解决方案。请记住为继承中的每个类提供一个克隆。

class Base
{
public:
    virtual ~Base() {}
    virtual Base *Clone() const
    {
        // code to copy stuff here
        return new Base(*this);
    }
};

class Derived : public Base
{
public:
    virtual Derived *Clone() const
    {
        // code to copy stuff here
        return new Derived(*this);
    }
};

0
投票

您可以使用虚拟 Clone 方法和辅助模板 CRTP 类来实现此接口来实现此目的:

class CBaseClass {
    //...
    virtual CBaseClass * Clone () = 0;
    std::unique_ptr<CBaseClass> UniqueClone () {
        return std::unique_ptr<CBaseClass>(Clone());
    }
    virtual std::shared_ptr<CBaseClass> SharedClone () = 0;
};

template <typename DERIVED>
class CBaseClassCRTP : public CBaseClass
{
    CBaseClass * Clone () {
        return new DERIVED(*static_cast<DERIVED *>(this));
    }
    std::shared_ptr<CBaseClass> SharedClone () {
        return std::make_shared<CbaseClass>(*static_cast<DERIVED *>(this));
    }
};

class CDerivedClass : public CBaseClassCRTP<CDerivedClass>
{
    //...
};

现在,每个派生类都获得一个由辅助类提供的

Clone
方法。


0
投票

使用 C++23 的显式对象参数,您可以完全在基类中完成此操作。

class CBaseClass
{
    template <typename Self>
    std::unique_ptr<Self> Clone(this const Self& self) const {
        return std::make_unique<Self>(self);
    }
…
};

有了这个,你真的不需要免费的功能克隆,但它很简单

std::unique_ptr<CBaseClass> Clone(const CBaseClass* base) {
    return Base ? Base->Clone() : nullptr;
}
© www.soinside.com 2019 - 2024. All rights reserved.