当您使用来自图书馆的摘要 class 时,您如何解决 "deep copy of an abstract class" 问题?

How do you solve the "deep copy of an abstract class" problem when you are working with an abstract class from a library?

我有一个指向抽象 class 的成员指针,我 运行 遇到了一个常见问题,我无法复制指针指向的内容,因为我不知道派生的是什么class指针指向。

在谷歌搜索解决方案时,我发现了这个问题:Copy constructor: deep copying an abstract class

它提出了这个确切的问题和答案 the virtual constructor idiom,这涉及到您将纯虚拟的 clone() 方法添加到抽象基础 class,然后您在所有派生 classes,因此它们 return 分配指向其实际类型的指针。

问题是这个解决方案对我不起作用,因为我需要复制的抽象类型来自库 (SFML)。这意味着我无法更改 class 来添加 clone() 方法。

如何在不改变抽象基础的情况下解决问题class?

为了这个答案,我们假设您正在使用 SFML 的 sf::Shape 摘要 class:

因此,您可能正在通过 sf::Shape 指针或引用处理 sf::CircleShapesf::ConvexShapesf::RectangleShape 对象。这些是具体的 classes.

您可以定义以下摘要 class ClonableShape:

,而不是通过 sf::Shape
struct ClonableShape {
    virtual ~ClonableShape() = default;

    virtual std::unique_ptr<ClonableShape> clone() const = 0;

    // extend at will with the member functions from sf::Shape, e.g.:
    virtual const sf::Vector2f& getPosition() const = 0;
    // ...
};

您可以使用此接口以多态方式克隆这些对象,方法是使用上面的具体 classes 实例化以下 class 模板:

template<typename T>
struct Clonable: T, ClonableShape {
    Clonable() = default;

    template<typename... Args>
    Clonable(Args... args): T(args...) {}

    std::unique_ptr<ClonableShape> clone() const override {
        return std::make_unique<Clonable<T>>(*this);
    }

    const sf::Vector2f& getPosition() const override {
        // careful not to call ClonableShape::getPosition() instead
        return T::getPosition();
    }   
};

也就是说,此解决方案依赖于 继承,但您可能需要考虑 组合 而不是 X.

最后,您可以多态地克隆形状对象:

std::unique_ptr<ClonableShape> shapeA(new Clonable<sf::RectangleShape>(sf::Vector2f{4, 4}));
std::unique_ptr<ClonableShape> shapeB = std::make_unique<Clonable<sf::CircleShape>>(4.f);

std::unique_ptr<ClonableShape> shapeC = shapeA->clone();
auto shapeD = shapeB->clone();

由于 sf::Shape publicly 派生自 sf::Drawable and there are several functions in the SFML library that accept a reference to sf::Drawable, e.g., sf::RenderWindow::draw(),您可能希望扩展上面的 ClonableShape 接口,以便能够隐式转换为 sf::Drawable&添加:

virtual operator sf::Drawable&() = 0;

然后在 Clonable<> 中将其覆盖为:

operator sf::Drawable&() override { return *this; }

这样,如果你有这样的函数:

void draw(sf::Drawable&);

您只需编写即可调用它:

draw(*shapeA);

X 如果不太依赖具体的 classes – 例如,如果你不工作直接使用 Clonable<sf::RectangleShape>Clonable<sf::CircleShape> – 然后你可能想从 inheritance 切换到 composition 因为你不会很大程度上取决于通过将具体形状作为 public 基础而继承的成员函数,而是取决于您在 ClonableShape:

中指定的那些
template<typename T>
class Clonable: public ClonableShape {
    T shape_; // as data member instead of public base
public:
    Clonable() = default;

    template<typename... Args>
    Clonable(Args... args): shape_(args...) {}

    std::unique_ptr<ClonableShape> clone() const override {
        return std::make_unique<Clonable<T>>(*this);
    }

    const sf::Vector2f& getPosition() const override {
        // careful not to call ClonableShape::getPosition() instead
        return shape_.getPosition();
    }

    // operator sf::Drawable&() override { return shape_; } 
};