c ++: точный тип содержащихся объектов без приведений - PullRequest
3 голосов
/ 04 мая 2011

Я получил классический пример иерархии фигур ...

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 ]

7 голосов
/ 04 мая 2011

Вы, вероятно, должны придерживаться виртуальных функций и иметь только одну функцию print_types

void print_types(Shape&)

Затем добавьте виртуальную функцию PrintName в базовый класс и переопределите ее в производном классе.

ЭтоЭто самый элегантный способ.

4 голосов
/ 04 мая 2011

Короткий ответ - нет, вызовы функций разрешаются во время компиляции. Так что (AFAIK) нет способа сделать это с вашими существующими бесплатными функциями.

Полагаю, вам придется инвестировать в механизм двойной отправки или делать то, что @Peter предлагает в своем ответе (что звучит более элегантно).

0 голосов
/ 04 мая 2011

Ответ на редактирование проблемы:

Лучшее решение - найти виртуальный метод, который доставит то, что требуется.Для обнаружения столкновений фигур, возможно, у вас может быть функция, которая преобразует каждую фигуру в многоугольник (набор линий) и выполняет обнаружение по ней.

C ++ использует тип, который вы объявили во время компиляции, чтобы выбрать, какой перегруженныйфункция для вызова;у него нет возможности сделать этот выбор во время выполнения.Вот почему вы видите «Shape, Shape» как вывод каждый раз.Есть способ выручить компилятор, но он будет утомительным.Попытайтесь преобразовать каждый 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 */;
}
0 голосов
/ 04 мая 2011

Я не могу назвать это элегантным, и у него есть несколько ловушек, но классический способ сделать это до dynamic_cast заключался в том, чтобы иметь функцию virtual, скажем virtual char* name(), в Shape и иметь каждый производныйкласс переопределяет эту функцию, чтобы вернуть правильное имя.

Самая очевидная ошибка заключается в том, что вы должны поддерживать все это вручную.

...