在 C++ 中干净地复制基类或子类的实例?

发布于 2024-09-29 05:51:32 字数 722 浏览 9 评论 0 原文

在简单的示例继承层次结构中:

class Food
{
    virtual ~Food();
};

class Fruit : public Food
{
    virtual ~Fruit();
};

class Apple: public Fruit
{
    virtual ~Apple();
}

class Vegetable: public Food
{
    virtual ~Vegetable();
}

我希望创建一个可以从其子类或基类实例克隆对象的方法:

Apple* apple1 = new Apple();
Apple* clone1 = apple1->clone();

Food* food1 = apple1;
Apple* clone2 = food1->clone();

我看到了该问题的一些可能的解决方案:

  • 使用多态性创建一个显式定义的虚拟函数每个子类。

  • 使用模板化工厂方法获取类的实例和子类的类型来创建新实例。

  • 为存储在其他地方的每个类和子类注册一个 id,以便在调用基类克隆函数时查找要创建的子

这些看起来都不理想,但我更倾向于第三种解决方案,因为它简化了克隆函数的调用,而不需要为每个子类编写定义(子类会有很多)。

但是,我非常愿意接受任何建议,有更好的方法吗?

In the trivial example inheritance hierarchy:

class Food
{
    virtual ~Food();
};

class Fruit : public Food
{
    virtual ~Fruit();
};

class Apple: public Fruit
{
    virtual ~Apple();
}

class Vegetable: public Food
{
    virtual ~Vegetable();
}

I wish to create a method that can clone an object from its subclass or baseclass instance:

Apple* apple1 = new Apple();
Apple* clone1 = apple1->clone();

Food* food1 = apple1;
Apple* clone2 = food1->clone();

I see a few possible solutions to the problem:

  • Use polymorphism to create a virtual function that is explicitly defined for each subclass.

  • Use a templated factory method to take an instance of a class and the type of the subclass to create the new instance.

  • Register an id with each class and subclass that is stored elsewhere to lookup which subclass to create on calling a baseclass clone function.

None of these seem ideal, but I'm leaning more towards the third solution as it simplifies calling the clone function without requiring a definition to be written for every subclasses (for which there will be a lot).

However, I'm very much open to any suggestions, are there better ways of doing this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

请叫√我孤独 2024-10-06 05:51:32

您可以使用CRTP自动实现Clone方法。

template<typename T, typename Derive> class CloneImpl : public Derive {
public:
    virtual Derive* clone() {
        return new T(static_cast<const T&>(*this));
    }
};
class Food {
public:
    virtual Food* clone() = 0;
    virtual ~Food() {}
};
class Fruit : public Food {
};
class Dairy : public Food {
};
class Apple : public CloneImpl<Apple, Fruit> {
};
class Banana : public CloneImpl<Banana, Fruit> {
};
class Cheese : public CloneImpl<Cheese, Dairy> {
};
class Milk : public CloneImpl<Milk, Dairy> {
};

在这种情况下,您始终可以调用 Clone() 来复制当前对象并在堆上进行新的分配,并且不需要在任何类中再次实现它。当然,如果您的克隆语义需要不同,那么您只需更改函数即可。

CRTP不仅可以为您实现clone(),它甚至可以在不同的继承层次结构之间实现clone()。

You can use the CRTP to automatically implement a Clone method.

template<typename T, typename Derive> class CloneImpl : public Derive {
public:
    virtual Derive* clone() {
        return new T(static_cast<const T&>(*this));
    }
};
class Food {
public:
    virtual Food* clone() = 0;
    virtual ~Food() {}
};
class Fruit : public Food {
};
class Dairy : public Food {
};
class Apple : public CloneImpl<Apple, Fruit> {
};
class Banana : public CloneImpl<Banana, Fruit> {
};
class Cheese : public CloneImpl<Cheese, Dairy> {
};
class Milk : public CloneImpl<Milk, Dairy> {
};

In this case, you can always call Clone() to copy the current object with a fresh allocation on the heap and you don't need to implement it again in any class. Of course, if your Clone semantics need to be different, then you can just alter the function.

Not only can the CRTP implement clone() for you, it can even do it between different inheritance hierarchies.

我乃一代侩神 2024-10-06 05:51:32

你的最后一个例子,……

Food* food1 = dynamic_cast<Food*>(apple1);
Apple* clone2 = f1->clone();

即使纠正了拼写错误,也不会起作用。您需要以另一种方式进行转换:

Food* food1 = apple1;
Apple* clone2 = dynamic_cast<Apple*>( f1->clone() );

除此之外,在 C++ 中克隆的实际解决方案是定义一个宏:

#define YOURPREFIX_IMPLEMENT_CLONING( Class )                       \
    virtual Class*                                                  \
        virtualCloneThatIsUnsafeToCallDirectly() const              \
    {                                                               \
        assert( typeid( *this ) == typeid( Class ) );               \
        return new Class( *this );                                  \
    }                                                               \
                                                                    \
    OwnershipPtr< Class >                                           \
        clone() const                                               \
    {                                                               \
        return OwnershipPtr< Class >(                               \
            virtualCloneThatIsUnsafeToCallDirectly()                \
            );                                                      \
    }

... 其中 OwnershipPtr 可能是例如 std::auto_ptr

然后您所要做的就是在每个应该支持克隆的类中放置一个宏调用。简单的。

也可以通过模板实现可重用克隆,但这对于实现和使用来说都更加复杂。您可以在我的博客文章 “混合通用克隆实现的 3 种方法”。然而,结论是宏是最实用的。

Your last example, ...

Food* food1 = dynamic_cast<Food*>(apple1);
Apple* clone2 = f1->clone();

... won't work, even with the speling eror corrected. You need the cast the other way:

Food* food1 = apple1;
Apple* clone2 = dynamic_cast<Apple*>( f1->clone() );

Apart from that, the practical solution to cloning in C++ is to define a macro:

#define YOURPREFIX_IMPLEMENT_CLONING( Class )                       \
    virtual Class*                                                  \
        virtualCloneThatIsUnsafeToCallDirectly() const              \
    {                                                               \
        assert( typeid( *this ) == typeid( Class ) );               \
        return new Class( *this );                                  \
    }                                                               \
                                                                    \
    OwnershipPtr< Class >                                           \
        clone() const                                               \
    {                                                               \
        return OwnershipPtr< Class >(                               \
            virtualCloneThatIsUnsafeToCallDirectly()                \
            );                                                      \
    }

... where OwnershipPtr might be e.g. std::auto_ptr.

Then all you have to do is to place a macro invocation in each class that should support cloning. Simple.

It's also possible to implement reusable cloning via templating, but that's more complicated both for the implementation and the usage. You can read about that in my blog posting "3 ways to mix in a generic cloning implementation". The conclusion is, however, that the macro is most practical.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文