在抽象基类中使用抽象类成员的最佳方法?

时间:2014-01-26 00:54:48

标签: c++ c++11

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

我有一个这样的课程:

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,这就是我需要强制转换的原因。

当然,我需要做一些错误处理,如果动态转换失败并返回一个空的shared_ptr。

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

这样可行,但使用起来更麻烦并引入堆分配。我也有shared_ptr开销(我会使用unique_ptr,但我需要动态投射)。

这是我做我想做的最合适的方式吗?

3 个答案:

答案 0 :(得分:3)

最好将接口保持为接口,而不是开始强制实现细节。所以只需要一个像我这样的空基类:

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

答案 1 :(得分:1)

你没有分享你想要做的一切,但这只是一种方式:

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

可能有理由说这不适合你,但没有更多的洞察力,我无法猜测是什么。

答案 2 :(得分:1)

为什么不简单地介绍一种从具体类中检索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);
    }
};

当你只能存储一个普通的旧成员时,通过一个指向base的指针存储,引入间接和向下转发以及引用计数开销似乎很荒谬。事实上,如果我正确地理解了这个问题,你可能会使用一个模板来生成具体的类并避免大量的样板:

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;