Механизм RTTI (run-time type identification), страница 2

Оператор typeidможно применить к выражениям:

1)  ссылке на класс

2)  разыменованному указателю (при этом, если значение указателя==0, вырабатывается исключение bad_typeid_exception)

3)  subscripted pointer (i.e. [ ]) (Note that it is generally not safe to use a subscript with a pointer to a polymorphic type.)

При этом, если даже в качестве выражения фигурирует ссылка или указатель на базовый (абстрактный) класс, результат typeid-оператора –const type_info& целевого класса.

..1.4.3 Пример:

class Shape{

public:

virtual void F()=0;

};

class Rect: public Shape{

public:

virtual void F(){cout<<”I’m Rect”<<endl;}

};

class Circle: public Shape{

public:

virtual void F(){cout<<”I’m Circle”<<endl;}

};

//Функция – фабрика фигур

Shape*    Make()

{

switch(rand()%2)

{

case 0:

return new Rect;

case 1:

return new Circle;

}

}

void main()

{

const int n = 10;

Shape* ar[n];

int nRects=0, nCircles=0;   //здесь подсчитаем – сколько чего насоздавалось

for(int i = 0; i<n; i++)

{

ar[i] = Make();    //создание очередного объекта

cout<<typeid(*ar[i]).name()<<endl;

if(typeid(*ar[i]) == typeid(Rect)) nRects++;

else nCircles++;

}

for(int i = 0; i<n; i++)

{

delete ar[i];

}

}

..1.5 dynamic_cast

Страуструп 462

Необходимость RTTI обусловлена тем, что при компиляции не всегда есть возможность выяснить: на какой целевой объект указывает указатель базового типа (или ссылается ссылка на базовый тип). Используя механизм RTTI, можно проверить: на какой фактический тип указывает указатель базового типа (или ссылка).

Когда идентификация типа бывает необходима – пример:

MFC предоставляет нам иерархию классов для разнообразнейших окон + то, что называется каркасом приложения – совокупность функций, обеспечивающая работу скелета Вашего приложения (в частности предусматривает реакции на запросы системы). Но Вы на базе классов MFC создаете свои классы, производные от предоставляемых классов. При этом каркасу приложения о Ваших классах в большинстве случаев ничего не знать не нужно.

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

Обычно наш производный объект передается системе (каркасу или посредством каркаса ОС), над его базовой частью совершается какая-то базовая работа и в какой-то момент (асинхронно) передается обратно. Когда система возвращает объект, она ничего не знает о нашей специализированной части => возвращается указатель на базовый объект => мы теряем информацию о действительном типе объектов, переданных системе, а затем возвращенных нам обратно.

Восстановление “потерянного” типа объекта требует, чтобы была возможность “спросить” объект о его типе => должна быть операция преобразования типа, которая возвращала бы корректный указатель, если объект действительно имеет целевой тип, или сообщение о невозможности преобразования.

Формат:

dynamic_cast<T*>(p)     //возвращает указатель типа Т*, если преобразование корректно, или 0

Пример:

class MyWnd : public CWnd{…};

void MyHandler(CWnd* p)

{

if(MyWnd* pMy = dynamic_cast<MyWnd*>(p) )

{

pMy->DoSmth();     //невиртуальная функция или получить посредством указателя данные своей производной части

}else{…} //не имеем права – объект другого типа

}

Замечание: для тех же целей можно было бы использовать и typeid – код более громоздкий:

void MyHandler(CWnd* p)

{

if(typeid(*p) == typeid(MyWnd) )

{

MyWnd* pMy=static_cast<MyWnd*>(p);

pMy->DoSmth();

}else{…} //не имеем права – объект другого типа

}

bool CRect::operator ==( CShape &r)//будет вызвана перегруженная функция для левого

//операнда. При этом необходимо убедиться, что справа

//операнд того же типа, иначе они сразу не равны!

{

if(CRect* p = dynamic_cast< CRect*>(&r))

{

return (

(m_bottom==p->m_bottom) &&

(m_top==p->m_top) &&

(m_left==p->m_left) &&

(m_right==p->m_right) );

}

else return false;

}

..1.6 Операторы typeid и dynamic_cast и наследование (проект Visual Studio Projects/RTTI)

class A{

int a;

public:

virtual void f() {}

};

class B : public A {int b;};

class C : public B{int c;};

{

A* pA = new C;

C* pC = dynamic_cast<C*>(pA);//!=0

B* pB = dynamic_cast<B*>(pA); //!=0

//Но!!!

bool b = typeid(*pC)==typeid(C);//true

b = typeid(*pC)==typeid(B);//false