我在设计一个允许我绘制各种形状的对象的类时遇到了问题。
Shape
class vector<Shape*> ShapeCollection
来存储派生对象,即Triangle,Square, Rectangle
在这一点上,我坚持认为类的设计应该是什么,因为单个'Drawing'类将执行绘图,消耗'Shape'类的对象。因为向量将包含相同基类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 :(得分:2)
你会使用多态。
void DrawShape() = 0;
中所述)这样你就可以在每个对象上调用DrawShape()
,即使它是作为Shape对象传递的。
替代品(注意:代码尚未经过测试):
函数指针,就像构建自己的vtable aka 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);
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
类继承自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
}
};
使用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。
答案 1 :(得分:1)
这是 经典演示,当您需要虚拟功能时。在基类中定义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);
}
};
另一种可能性是使用访客模式。当您需要/想要遍历更复杂的结构而不是简单的线性序列时,这通常是 ,但也可以在这里使用。这已经足够复杂了,这可能有点多了,但是如果你搜索“访客模式”,你应该提供相当数量的材料。