绘图对象 - 更好的类设计
Drawing objects - Better class design?
我在设计一个允许我绘制各种形状对象的类时遇到了问题。
- 形状是基类
- 三角形、正方形、矩形是从类派生
Shape
类 - 我有一个存储派生对象的
vector<Shape*> ShapeCollection
,即Triangle,Square, Rectangle
- 一旦我从矢量中选择一个对象,我需要将对象绘制到屏幕上。
在这一点上,我被困在一个类的设计应该是什么,作为一个"绘图"类将进行绘图,消耗"形状"类的对象。由于向量将包含相同基类的不同对象Shape
.因为我有一个线程可以从矢量中拾取一个对象,一旦我有一个对象,我必须能够正确绘制它。
所以下面或多或少是我说的
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函数将进行绘图。
这一定是已经解决的问题。必须有更好的方法来做到这一点,因为所有这些开关语句都必须以某种方式消失!
非常感谢任何指导。
谢谢
@Adrian和@Jerry建议使用虚函数,我想到了,但我需要让我的绘图远离基类Shape
你会使用多态性。
- 在你的基类中创建一个纯虚函数(即在声明函数时将其分配给 0,如
void DrawShape() = 0;
( - 在派生类中声明和定义该函数。
这样,即使它作为 Shape 对象传递,也可以对每个对象调用 DrawShape()
。
替代方案(注意:代码尚未测试(:
-
函数指针,就像构建自己的 vtable aka 委托。
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);
-
Functor,这是一个覆盖 operator(( 的类,也类似于委托
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);
从另一个类继承,该类使用 模板化基类实现所需的函数(IIRC,这是在 ATL 中完成的(。这只是滚动你自己的硬编码vtable,称为奇怪的重复类型模式(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); } };
其他示例可以在这里和这里找到。
让
draw
类继承自从基类继承shape
type of 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 } };
使用
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
已被证明存在性能问题。
这几乎是你想要一个虚拟函数的经典演示。在基类中定义一个draw
,然后在每个派生类中重写它。然后,若要绘制所有对象,请单步执行集合并为每个对象调用draw()
成员。
class shape {
// ...
virtual void draw(canvas &c) = 0;
};
class square : public shape {
int x, y, size;
// ...
virtual void draw(canvas &c) {
c.move_to(x, y);
c.draw_to(x+size, y);
c.draw_to(x+size, y+size);
c.draw_to(x, y+size);
c.draw_to(x, y);
}
};
。等等,对于您关心的每种形状。
编辑:使用策略类,你最终会得到沿着这条线模糊的代码:
template <class draw>
class shape {
// ...
virtual void draw(canvas &c) = 0;
};
template <class d>
class square : public shape<d> {
// ...
virtual void draw(canvas &c) {
d.square(x, y, size, c);
}
};
另一种可能性是使用访客模式。当您需要/想要遍历更复杂的结构而不是简单的线性序列时,通常会使用此方法,但也可以在此处使用。这已经足够复杂了,在这里可能有点多,但如果你搜索"访客模式",你应该找到相当数量的材料。
- 什么更好?返回对象指针列表?或返回指向对象列表的指针?
- 动态分配对象中的字段-动态分配更好还是静态分配更好?C++
- 按引用或值传递链表对象更好吗?
- 是否有更好的方法来封装成员对象可以访问的共享存储池?
- 在对象序列化期间添加额外参数是否有更好的方法?
- 将对象从一个 std::d eque 移动到另一个的更好方法
- 构造具有大量数据的对象的更好方法(C++)
- 在C++中创建不可变对象的更好方法
- 使用成员函数更改对象或返回并分配它是否被认为是更好的做法?
- 更好的是:存储对象与存储指针
- 全局对象是否提供比多个本地实例更好的性能
- 对成员类/对象变量使用指针更好吗
- 哪种设计更好:提供对所属对象的直接访问,或者为所属对象提供所属对象转发方法
- 在C++中使用指向数组中对象的指针更好吗
- 在类中包含对象或更好地从该类派生
- 对对象大小的“static_assert”更好的消息
- 是否有任何情况将对象标记为 const 会导致更好的优化代码(使用优化编译时)
- 将对象副本添加到容器的更好方法
- 是否有更好或正确的方法在 C++ 中使用带有 vector 的对象
- 创建对象数组然后有效删除它们的更好方法