我在设计一个允许我绘制各种形状的对象的类时遇到了问题。
- Shape 是基类
- Triangle、Square、Rectangle 是来自
Shape
的派生类类 - 我有一个
vector<Shape*> ShapeCollection
存储派生对象,即Triangle,Square, Rectangle
- 一旦我从 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
最佳答案
你会使用多态性。
- 在您的基类中创建一个纯虚函数(即在声明函数时将其赋值为 0,如
void DrawShape() = 0;
) - 在您的派生类中声明并定义该函数。
这样您就可以在每个对象上调用 DrawShape()
,即使它是作为 Shape 对象传递的。
备选方案(注意:代码尚未经过测试):
函数指针,这就像构建您自己的 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);
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);
使用模板化基类(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); } };
让您的
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 .
关于c++ - 绘图对象 - 更好的类设计?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17246016/