Проверка типа объекта в C++11

У меня есть класс B, который наследуется от A.

class A
{
};

class B : public A
{
};

И у меня есть три объекта.

A* a = new A();
A* a2 = new B();
B* b = new B();

Я хотел бы проверить, является ли a объектом типа A, a2 является объектом типа B (не A), а b является объектом типа B.

Я пробовал типизированное сравнение, но это не дает мне правильного ответа.

cout << (typeid(*a) == typeid(A)) << endl; // -> 1
cout << (typeid(*a2) == typeid(A)) << endl; // -> 1
cout << (typeid(*b) == typeid(A)) << endl; // -> 0

cout << (typeid(*a) == typeid(B)) << endl; // -> 0
cout << (typeid(*a2) == typeid(B)) << endl; // -> 0
cout << (typeid(*b) == typeid(B)) << endl; // -> 1

Я пробовал динамическое литье, но получил ошибку компиляции.

B* derived = dynamic_cast<B*>(a);
if (derived) {
    cout << "a is B";
}
derived = dynamic_cast<B*>(a2);
if (derived) {
    cout << "a2 is B";
}
derived = dynamic_cast<B*>(b);
if (derived) {
    cout << "b is B";
}

typename.cpp: In function 'int main(int, char**)':
typename.cpp:27:36: error: cannot dynamic_cast 'a' (of type 'class A*') to type 'class B*' (source type is not polymorphic)
     B* derived = dynamic_cast<B*>(a);
                                    ^
typename.cpp:31:34: error: cannot dynamic_cast 'a2' (of type 'class A*') to type 'class B*' (source type is not polymorphic)
     derived = dynamic_cast<B*>(a2);

Я использовал статическое литье, но ошибся в ответе.

B* derived = static_cast<B*>(a);
if (derived) {
    cout << "a is B"; // -> YES
}
derived = static_cast<B*>(a2);
if (derived) {
    cout << "a2 is B"; // -> YES
}
derived = dynamic_cast<B*>(b);
if (derived) {
    cout << "b is B"; // -> YES
}

Как правильно определить тип объекта в C++11?


person prosseek    schedule 20.06.2013    source источник
comment
*a имеет тип A, а *b имеет тип B согласно их объявлению.   -  person K-ballo    schedule 20.06.2013
comment
Динамическое приведение будет работать только с виртуальным, но в вашем коде нет виртуальной функции.   -  person Astro - Amit    schedule 20.06.2013
comment
Лучшее минимальное решение, похожее на ваш код: coliru.stacked-crooked.com/   -  person chris    schedule 20.06.2013


Ответы (1)


Некоторые классы полиморфны, некоторые неполиморфны.

Полиморфный класс имеет одну или несколько виртуальных функций (возможно, унаследованных), неполиморфный класс не имеет виртуальных функций.

Ваши A и B не полиморфны.

Полиморфная версия A и B будет демонстрировать желаемое поведение:

#include <iostream>
#include <typeinfo>

using namespace std;

struct A
{
    virtual ~A() {}; // add virtual function
};

class B : public A
{
};

A* a = new A();
A* a2 = new B();
B* b = new B();

int main()
{
    cout << (typeid(*a) == typeid(A)) << endl; // -> 1
    cout << (typeid(*a2) == typeid(A)) << endl; // -> 0 <-- CHANGED
    cout << (typeid(*b) == typeid(A)) << endl; // -> 0

    cout << (typeid(*a) == typeid(B)) << endl; // -> 0
    cout << (typeid(*a2) == typeid(B)) << endl; // -> 1 <-- CHANGED
    cout << (typeid(*b) == typeid(B)) << endl; // -> 1
}

Экземпляры полиморфного класса сохраняют динамический тип своего наиболее производного объекта во время выполнения.

(В вашем примере a2 имеет тип указатель-на-A и указывает на объект типа A, однако этот объект является только подобъектом базового класса наиболее производного объекта типа B. То, что вы хотите получить, — это тип этого наиболее производного объекта B при запросе a2. Для этого вам нужен полиморфный класс.)

Именно так полиморфные классы поддерживают dynamic_cast и typeid самого производного объекта (а также диспетчеризацию виртуальных функций).

Неполиморфные классы не имеют этой информации, поэтому они могут сообщать только о статическом типе, известном во время компиляции. Неполиморфные классы более компактны и эффективны, чем полиморфные классы. Вот почему не все классы C++ полиморфны. Язык оставляет на усмотрение программиста выбор компромисса между производительностью и функциональностью. Например:

struct X { int x; };
struct Y : X {};
struct Z : Y {};

В моей системе неполиморфный Z равен sizeof(Z) == 4 bytes, как и int.

struct X { int x; virtual ~X() {}; };
struct Y : X {};
struct Z : Y {};

Теперь, сделав Z полиморфным, sizeof(Z) == 16 bytes. Таким образом, массив Z теперь на 300% больше, потому что каждый экземпляр Z должен хранить информацию о своем типе во время выполнения.

person Andrew Tomazos    schedule 20.06.2013
comment
stackoverflow.com/questions/17221668/ - person prosseek; 20.06.2013
comment
@Sharth: Нет, это неправда; вы не получите его в любом случае. - person Lightness Races in Orbit; 20.06.2013
comment
@LightnessRacesinOrbit, @prosseek: Спасибо за исправление! Я думал, что это одна из главных причин дела = default. - person Bill Lynch; 21.06.2013
comment
@sharth: Это было бы неплохо, но, увы, нет. - person Lightness Races in Orbit; 21.06.2013
comment
Комментарии относятся к уже удаленному неверному утверждению о том, что использование = default вместо {} будет препятствовать созданию конструктора перемещения по умолчанию. - person Andrew Tomazos; 21.06.2013
comment
Обратите внимание, что вы можете вернуть генерацию конструктора перемещения по умолчанию с помощью A(A&&) = default; - person Ben Voigt; 24.06.2013