gpt4 book ai didi

c++ - 绘图对象 - 更好的类设计?

转载 作者:太空狗 更新时间:2023-10-29 21:23:53 26 4
gpt4 key购买 nike

我在设计一个允许我绘制各种形状的对象的类时遇到了问题。

  1. Shape 是基类
  2. Triangle、Square、Rectangle 是来自Shape 的派生类类
  3. 我有一个 vector<Shape*> ShapeCollection存储派生对象,即 Triangle,Square, Rectangle
  4. 一旦我从 vector 中选取了一个对象,我就需要将该对象绘制到屏幕上。

在这一点上,我对类的设计感到困惑,因为单个“绘图”类将使用“形状”类的对象进行绘图。由于 vector 将包含同一基类的不同对象 Shape .因为我有一个从 vector 中拾取对象的线程,所以一旦我有了一个对象,我就必须能够正确地绘制它。

下面大概就是我要说的

class Drawing
{
public:
void Draw(Shape* shape, string objectName)
{
// Now draw the object.
// But I need to know which Object I am drawing or use
// switch statements to identify somehow which object I have
// And then draw. I know this is very BAD!!!
// e.g.
switch(objectName)
{
case "rectangle":
DrawRectangle((Rectangle*) shape)
break;
//Rest of cases follow
}
}
}

因为我将有一个 DrawSquare、DrawTriangle 函数来进行绘图。

这一定是已经解决的问题。必须有更好的方法来做到这一点所有这些 switch 语句都必须以某种方式消失!

非常感谢任何指导。

谢谢


@Adrian 和@Jerry 建议使用虚函数,我想到了,但我需要让我的 Drawing 远离基类 Shape

最佳答案

你会使用多态性。

  1. 在您的基类中创建一个纯虚函数(即在声明函数时将其赋值为 0,如 void DrawShape() = 0;)
  2. 在您的派生类中声明并定义该函数。

这样您就可以在每个对象上调用 DrawShape(),即使它是作为 Shape 对象传递的。

备选方案(注意:代码尚未经过测试):

  1. 函数指针,这就像构建您自己的 vtable 也就是委托(delegate)。

    struct square
    {
    void (*draw)(square&);
    };

    void drawSquare(square& obj)
    {
    // draw square code
    // there is no 'this'. must access members via `obj`.
    }

    square s;
    s.draw = drawSquare;
    s.draw(s);
  2. Functor,它是一个覆盖 operator() 的类,也像一个委托(delegate)

    struct square
    {
    // Note that std::function can hold a function pointer as well as a functor.
    function<void(square&)> draw;
    };

    struct drawSquare
    {
    void oparator()(square& obj)
    {
    // draw square code
    // there is no 'this'. must access members via `obj`.
    }
    };

    square s;
    square s.draw = drawSquare();
    s.draw(s);

    注意:1 和 2 也可以用 lambda 函数初始化:

    square s;
    s.draw = [](square& obj) {
    // draw square code
    // there is no 'this'. must access members via `obj`.
    };
    s.draw(s);

    注意:1 可以使用模板完成:

    struct square;

    template <void (*DRAW)(square&)>
    struct square
    {
    void draw()
    {
    DRAW(*this);
    }
    };

    void drawSquare(square& obj)
    {
    // draw square code
    // there is no 'this'. must access members via `obj`.
    }

    square s<&drawSquare>;
    s.draw();

    注意:2 也可以使用模板完成:

    template <typename DRAW>
    struct square
    {
    void draw()
    {
    // First set of parentheses instantiate the DRAW object.
    // The second calls the functor.
    DRAW()(*this);
    }
    };

    struct drawSquare
    {
    void oparator()(square& obj)
    {
    // draw square code
    // there is no 'this'. must access members via `obj`.
    }
    };

    square s<drawSquare>;
    s.draw();

    或者,这将允许传递有状态仿函数:

    template <typename DRAW>
    struct square
    {
    DRAW draw;
    };

    struct drawSquare
    {
    void operator()(square& obj)
    {
    // draw square code
    // there is no 'this'. must access members via `obj`.
    }
    };

    square s<drawSquare>;
    s.draw = drawSquare();
    s.draw(s);
  3. 使用模板化基类(IIRC,这是在 ATL 中完成的)从另一个实现您想要的功能的类继承。这只是滚动您自己的硬编码 vtable,称为 Curiously Recurring Type Pattern (CRTP)。

    template <class D>
    struct shape
    {
    inline void draw() { return static_cast<D&>(*this).draw(); }
    };

    void draw(square& obj)
    {
    // draw square code
    // No 'this' available. must access shape members via `obj`.
    }

    struct square : public D<square>
    {
    void draw()
    {
    drawSquare(*this);
    }
    };

    可以找到其他例子herehere .

  4. 让您的 draw 类继承自 type of shape 类,后者继承自 shape 基类。

    struct shape
    {
    virtual void draw() = 0;
    };

    struct square : public shape
    {
    };

    struct drawSquare : public square
    {
    virtual void draw()
    {
    // draw square code
    // you access the square's public or protected members from here
    }
    };
  5. 使用 std::unordered_map

    #include <unordered_map>
    #include <typeinfo>
    #include <functional>

    struct shape { };

    struct square : public shape { };

    void drawSquare(shape& o)
    {
    // this will throw an exception if dynamic cast fails, but should
    // never fail if called from function void draw(shape& obj).
    square& obj = dynamic_cast<square&>(o);

    // draw square code
    // must access shape members via `obj`.
    }

    std::unordered_map<size_t, std::function<void(shape&)>> draw_map
    {
    { type_id(square).hash(), drawSquare }
    };

    void draw(shape& obj)
    {
    // This requires the RTTI (Run-time type information) to be available.
    auto it = draw_map.find(type_id(obj).hash());

    if (it == draw_map.end())
    throw std::exception(); // throw some exception
    (*it)(obj);
    }

    注意:如果您使用的是 g++ 4.7,请注意 unordered_map 已显示有 performance issues .

关于c++ - 绘图对象 - 更好的类设计?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17246016/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com