c ++:没有强制转换的包含对象的确切类型

时间:2011-05-03 21:05:51

标签: c++ oop inheritance polymorphism covariance

我得到了经典的Shape层次结构示例......

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;
};

一个Shapes容器,填充矩形和圆圈

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);

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

4 个答案:

答案 0 :(得分:7)

你应该坚持使用虚函数,并且只有一个print_types函数

void print_types(Shape&)

然后将虚拟PrintName函数添加到基类并在派生类中重写它。

这是最优雅的方式。

答案 1 :(得分:4)

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

我相信你必须投资double-dispatch mechanism,或者做@Peter在答案中的建议(听起来更优雅)。

答案 2 :(得分:0)

我不能称之为优雅,它有几个陷阱,但在dynamic_cast之前执行此操作的经典方法是virtual函数,例如virtual char* name(),{ {1}}并让每个派生类重写该函数以返回正确的名称。

最明显的缺陷是你必须手工维护整个事情。

答案 3 :(得分:0)

回应问题的编辑:

最好的解决方案仍然是找到一种能够提供所需要的虚拟方法。为了检测形状碰撞,也许您可​​以使用一个函数将每个形状转换为多边形(一组线)并对其进行检测。

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

我并不是在鼓吹这个;你可以看到它只是两个形状失控,想象你添加更多的丑陋!它仍然显示了如何做你在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 */;
}