在抽象基类中与抽象类成员合作的最佳方法

Best way to work with an abstract class member in an abstract base class?

本文关键字:最佳 方法 成员 抽象类 抽象 基类      更新时间:2023-10-16

我对问题标题不满意,但我无法很好地描述它。为了简洁起见,我将实施在班级声明中。

我有这样的课程:

class VisibleObject {
  public:
    void draw(sf::RenderWindow& rw) {
        rw.draw(*shape.get());
    }
    virtual void setSize(sf::Vector2f) = 0;
  protected:
    std::shared_ptr<sf::Shape> shape;
}

sf::Shape是一个抽象类。然后我有一个类似的派生课程:

class Brick : VisibleObject {
    Brick() {
        shape.reset(new sf::RectangleShape());
    }
    void setSize(sf::Vector2f newSize) {
        std::dynamic_pointer_cast<sf::RectangleShape>(shapes).get()->setSize(newSize);
    }
}

sf::RectangleShape()是一个具体类,从 sf::ShapesetSize()继承而不是 sf::Shape,这就是为什么我需要施放。

当然,如果动态铸件失败并返回一个空的共享_ptr。

,我需要做一些错误处理。

我这样做是因为我希望能够仅定义draw方法一次,因为在这个简单的游戏中,每个对象都会以这种方式绘制其成员。最初,我将形状排除在基类之外,例如砖只有自己的私人sf::RectangleShape可以在堆栈上实例化;这很干净,但是必须针对每种对象类型重写绘图方法。

这起作用,但与之合作并引入了堆分配更丑陋。我也有shared_ptr开销(我会使用unique_ptr,但我需要动态铸造)。

这是最合适的方法来做我要做的事情吗?

可能最好将接口保持接口,而不是开始强制执行实现详细信息。因此,只要拥有一个空的基类:

class VisibleObject
{
public:
    ~VisibleObject() {}
    virtual void draw(sf::RenderWindow & window) = 0;
    virtual void setSize(sf::Vector2f const & size) = 0;
};

您可以将形状存储粘贴到实现此接口的混凝土类中。

此外,Shape应该提供虚拟调整大小方法:

class Shape
{
public:
    virtual ~Shape() {}
    virtual void resize(sf::Vector2f const & size) = 0;
};

现在,您可以将VisibleShapeObject作为中间级类别进行:

class VisibleShapeObject : public VisibleObject
{
public:
    virtual void draw(sf::RenderWindow & window) override final
    {
        window.draw(*shape_);
    }
    virtual void setSize(sf::Vector2f const & size) override final
    {
        shape_->resize(size);
    }
protected:
    std::shared_ptr<Shape> shape_;    // or unique_ptr<Shape>
};

而不是在std::shared_ptr<sf::Shape>中强制存储,为什么不简单地引入一种从混凝土类中检索sf::Shape&的方法?

class VisibleObject {
    virtual sf::Shape& getShape() = 0;
public:
    void draw(sf::RenderWindow& rw) {
        rw.draw(getShape());
    }
    virtual void setSize(sf::Vector2f) = 0;
};
class Brick : VisibleObject {
    sf::RectangleShape shape;
    sf::Shape& getShape() override { return shape; }
public:
    void setSize(sf::Vector2f newSize) override {
        shape.setSize(newSize);
    }
};

通过指向底座的指针,引入间接和沮丧和参考数量的开销,这似乎是荒谬的,当时您可以存储一个普通的旧会员。实际上,如果我正确理解问题,您可能可以使用模板生成具体类并避免大量的样板:

class VisibleObject {
public:
    virtual ~VisibleObject() {}
    virtual void draw(sf::RenderWindow&) = 0;
    virtual void setSize(sf::Vector2f) = 0;
};
template <typename Shape>
class VisibleConcreteObject : public VisibleObject {
    Shape shape;
public:
    void draw(sf::RenderWindow& rw) override /* final? */ {
        rw.draw(shape);
    }
    void setSize(sf::Vector2f newSize) override /* final? */ {
        shape.setSize(newSize);
    }
};
typedef VisibleConcreteObject<sf::RectangleShape> Brick;

您尚未共享您要做的一切,但这是一种方法:

template<ShapeT>
class VisibleObject {
public:
    void draw(sf::RenderWindow& rw) {
          rw.draw(*shape.get());
    }
    virtual void setSize(sf::Vector2f) = 0;
protected:
    std::shared_ptr<ShapeT> shape;
    void reset(ShapeT* shape) {
        this->shape = shape;
    }
}
class Brick : VisibleObject<sf::RectangleShape> {
    Brick() {
        shape.reset(new sf::RectangleShape());
    }
    void setSize(sf::Vector2f newSize) {
       shape->setSize(newSize);
    }
}

可能有原因为什么这对您不起作用,但是没有更多的见解,我无法猜测什么。