2011-05-03 46 views
3

我得到了经典形状层次结构示例...C++:没有包含的对象的确切类型转换

struct Shape { // abstract type 
    Shape (int x, int y); 

    int x; 
    int y; 
}; 

struct Rectangle : public Shape { 
    Rectangle (int x, int y, int w, int h); 

    int w; 
    int h; 
}; 

struct Circle : public Shape { 
    Circle (int x, int y, int r); 

    int r; 
}; 

一个形状的容器中,填充有矩形和圆形

std::list<Shape*> container; 

和打印功能(在我的情况下,这些是碰撞检测功能)

void print_types (Shape&, Shape&) { 
    std::cout << "Shape, Shape" << std::endl; 
} 

void print_types (Rectangle&, Rectangle&) { 
    std::cout << "Rectangle, Rectangle" << std::endl; 
} 

void print_types (Rectangle&, Circle&) { 
    ... 

当然,当我这样做时:

std::list<Shape*> it; 
Rectangle r (0, 0, 32, 32); 

for (it = container.begin(); it != container.end(); it++) 
    print_types(r, **it); 

我不想只打印“形状,形状”线。我知道虚拟方法,dynamic_casts和访问者。但是如果没有这些解决方案并保留我的外部功能,是否有任何优雅的方式可以摆脱它?

+1

优雅的方法是使用它设计的语言。这意味着“虚拟方法,dynamic_casts和访客”。 – 2011-05-03 21:12:59

+1

@Mark:理想情况下不是dynamic_casts,尽管... – 2011-05-03 21:18:11

+0

@Oli,该语言的每个功能都是有原因的。我曾经找到一个dynamic_cast的例子,如果你不同意,你可以在那里留下一个注释:http://Marketing.com/questions/28080/how-bad-is-dynamic-casting/303312#303312 – 2011-05-03 21:21:38

回答

7

你或许应该坚持使用虚函数和只有一个print_types功能

void print_types(Shape&) 

然后添加一个虚拟PrintName函数的基类,并在派生类中重写它。

这是最优雅的方式。

+0

+1:这是比我的双重发送建议更清洁的解决方案。 – 2011-05-03 21:14:45

+0

事实上,我不是打印类型,而是检查表单之间的碰撞......也许我应该编辑我的初始文章。 – Simon 2011-05-03 21:20:51

4

简单的答案是否定的,函数调用在编译时解决。所以没有办法(AFAIK)用你现有的免费功能来做到这一点。

我相信你将不得不投资于double-dispatch mechanism,或做什么@Peter在他的回答表明,(这听起来更优雅)。

+0

我已经尝试过这种解决方案,它可以很好地工作,但我一直在寻找一种更“简单”的方法来做到这一点。 – Simon 2011-05-03 21:17:36

0

我不能把它的优雅,它有几个陷阱,但经典的方式dynamic_cast之前做到这一点是有一个virtual功能,说virtual char* name(),在Shape并有每个派生类重写该函数返回正确的名字。

最明显的缺陷是,你必须保持手的整个事情。

0

应对的问题编辑:

最好的解决办法还是找到,将提供需要什么虚方法。为了检测形状碰撞,也许你可以有一个函数将每个形状转换成多边形(一组线)并对其进行检测。

C++使用您在编译时声明的类型来选择要调用的重载函数;它不具备在运行时进行选择的能力。这就是为什么每次看到“形状,形状”作为输出的原因。有一种方法可以帮助编译器解决问题,但这将会很乏味。尝试每一种形状*转换为相应的类型,如果它成功,你可以调用一个更具体的功能。

我并不真正提倡这一点;你可以看到它是如何随着两种形状而变得无法控制的,想象你在添加更多东西时会有多丑!它仍然展示了如何去做你在C++中所要做的事情。

void print_types (Rectangle*, Rectangle*) { 
    std::cout << "Rectangle, Rectangle" << std::endl; 
} 

void print_types (Rectangle*, Circle*) { 
    ... 

void print_types (Rectangle* left, Shape* right) { 
    Rectangle* rightRect = dynamic_cast<Rectangle*>(right); 
    if (rightRect != NULL) { 
     print_types(left, rightRect); 
     return; 
    } 
    Circle* rightCirc = dynamic_cast<Circle*>(right); 
    if (rightCirc != NULL) { 
     print_types(left, rightCirc); 
     return; 
    } 
    throw /* something to indicate invalid shape */; 
} 

void print_types (Circle* left, Shape* right) { 
    ... 

void print_types (Shape* left, Shape* right) { 
    Rectangle* leftRect = dynamic_cast<Rectangle*>(left); 
    if (leftRect != NULL) { 
     print_types(leftRect, right); 
     return; 
    } 
    Circle* leftCirc = dynamic_cast<Circle*>(left); 
    if (leftCirc != NULL) { 
     print_types(leftCirc, right); 
     return; 
    } 
    throw /* something to indicate invalid shape */; 
}