LINUX.ORG.RU

Динамическое определение типа объекта в C++

 ,


0

1

Здравствуйте. Собственно, имеется пример такого кода:

#include <iostream>

using namespace std;

class A
{

};

class B : public A
{

};


int main()
{
    A a;
    B b;
    A* arr[2] = {&a, &b};
    for(int i = 0; i < 2; ++i)
    {
        // Вот тут код определения типа класса. К какому классу принадлежит этот объект
        // Допустим, если *arr[i] является обьектом типа A, пусть выводит 1, если B, то 2, иначе 3
    }
}

Как такое сделать?

#include <iostream>

using namespace std;

class A
{
public:
    virtual ~A () {};
};

class B : public A
{

};


int main()
{
    A a;
    B b;
    A* arr[2] = {&a, &b};
    for(int i = 0; i < 2; ++i)
    {
        if (dynamic_cast<A*>(arr[i])) {
            cout << 1;
        }
        if (dynamic_cast<B*>(arr[i])) {
            cout << 2;
        }
        cout << endl;
    }
}
jeuta ★★★★
()

Обычно в таких случаях для большей производительности в базовый класс вводится функция, возвращающая идентификатор типа производного класса. Но конечно можно использовать и dynamic_cast / typeid.

m0rph ★★★★★
()

Добавить в A виртуальную функцию, которая делает то, что нужно, а в B её перекрыть.

Begemoth ★★★★★
()
Ответ на: комментарий от zamazan4ik

не должна

если у тебя иерархия сложная, ты устанешь разбираться с dynamic_cast'ами, лучше сделай виртуальный метод type(), который выведет то, что тебе нужно у каждого класса

jeuta ★★★★
()
Ответ на: комментарий от zamazan4ik

не совсем верно получается. Программа выводит: 1 12.
А должна: 1 2

Просто в данном конкретном примере arr[2] можно преобразовать как к A*, так и к B*. Если же у тебя A будет абстрактным классом, от которого ты родишь B и C и будешь проверять уже их объекты, то такой подход будет работать.

m0rph ★★★★★
()

Решил проблему вот так:

#include <iostream>
#include <typeinfo>

using namespace std;

class A
{
public:
    virtual ~A () {};
};

class B : public A
{

};


int main()
{
    A a;
    B b;
    auto ta = typeid(a).name(), tb = typeid(b).name();
    A* arr[2] = {&a, &b};
    for(int i = 0; i < 2; ++i)
    {
        if (typeid(*arr[i]).name() == ta) {
            cout << 1;
        }
        else if (typeid(*arr[i]).name() == tb) {
            cout << 2;
        }
        cout << endl;
    }
}

Какие есть минусы у этого подхода и имеет ли он право на жизнь?

zamazan4ik ★★
() автор топика

Лучше без ртти сделай виртуальный метод, который вернёт 0 на родителе и указатель на себя в ребёнке.

gavlig ★★★
()
Ответ на: комментарий от zamazan4ik

минусы: производительность, по крайней мере debug-сборок, усложнённость клиентского кода (того, что у тебя в main)

сравни с:

#include <iostream>

using namespace std;

class A
{
public:
    virtual int getType () { return 1; }
};

class B : public A
{
    int getType () { return 2; }
};


int main()
{
    A a;
    B b;
    A* arr[2] = {&a, &b};
    for(int i = 0; i < 2; ++i)
    {
        cout << arr[i]->getType() << endl;
    }
}

jeuta ★★★★
()
Последнее исправление: jeuta (всего исправлений: 1)
Ответ на: комментарий от m0rph

Либо упорядочить проверки в обратном порядке наследования (т.е. сначала проверять подкласс B, потом суперкласс A). Естественно, цепочкой if ... else if ... else ..., а не независимыми if'ами.

Впрочем, правильней всего, конечно, перенести этот код в методы классов и не морочить голову.

korvin_ ★★★★★
()
Ответ на: комментарий от jeuta

1. RTTI сам по себе тяжелый.
2. RTTI есть не всегда.
3. Тут он нафиг не нужен.

andreyu ★★★★★
()

а для чего тебе вообще это? Какое применение? Суть полиморфизма в том, что бы со всеми объектами работать одинаково, а свое поведение определяют они сами.

CatsCantFly
()
Ответ на: комментарий от CatsCantFly

Пример такого поведения - обход AST,с целью оптимизации констант. Типа превратить a * c + ((2 -1) * 3) => a * c + 3, что бы ускорить вычисления.

Очевидно, что оптимизировать можно только выражения, содержащие только константы. Тогда как вычисляются элементы такого дерева через примерно такой интерфейс:

struct Expression {
    virtual double eval() = 0;
};

Но, даже тут лучше использовать ручную реализацию двойной диспетчеризации.

pon4ik ★★★★★
()
Последнее исправление: pon4ik (всего исправлений: 2)
Ответ на: комментарий от mashina

А как это сделать? просто на typeid ругается, что мол private он. Разве имён недостаточно? они ж уникальны

zamazan4ik ★★
() автор топика
Ответ на: комментарий от CatsCantFly

я этот вопрос задал только из-за академического интереса, ничего более. Но, быть может, и пригодится когда-нибудь. Как pon4ik пишет ниже, например

zamazan4ik ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.