Язык программирования C++. Вводный курс

Класс type_info


Точное определение класса type_info

зависит от реализации, но некоторые его характерные черты остаются неизменными в любой программе на C++:

class type_info {

   // ïðåäñòàâëåíèå çàâèñèò îò ðåàëèçàöèè

private:

   type_info( const type_info& );

   type_info& operator= ( const type_info& );

public:

   virtual ~type_info();

   int operator==( const type_info& );



   int operator!=( const type_info& );

   const char * name() const;

};

Поскольку копирующие конструктор и оператор присваивания – закрытые члены класса type_info, то пользователь не может создать его объекты в своей программе:

#include <typeinfo>

type_info t1;  // îøèáêà: íåò êîíñòðóêòîðà ïî óìîë÷àíèþ

               // îøèáêà: êîïèðóþùèé êîíñòðóêòîð çàêðûò

type_info t2 (typeid( unsigned int ) );

Единственный способ создать объект класса type_info – воспользоваться оператором typeid.

В классе определены также операторы сравнения. Они позволяют сравнивать два объекта type_info, а следовательно, и результаты, возвращенные двумя операторами typeid. (Мы говорили об этом в предыдущем подразделе.)

typeid( re )  == typeid( manager )     // èñòèííî

typeid( *pe ) != typeid( employee )    // ложно

Функция name()

возвращает C-строку с именем типа, представленного объектом type_info. Этой функцией можно пользоваться в программах следующим образом:




#include <typeinfo>

int main() {

   employee *pe = new manager;

   // ïå÷àòàåò: "manager"

   cout << typeid( *pe ).name() << endl;
}

Для работы с функцией-членом name()

нужно включить заголовочный файл <typeinfo>.

Имя типа – это единственная информация, которая гарантированно возвращается всеми реализациями C++, при этом используется функция-член name() класса type_info. В начале этого раздела упоминалось, что поддержка RTTI зависит от реализации и иногда в классе type_info

бывают дополнительные функции-члены. Чтобы узнать, каким образом обеспечивается поддержка RTTI в вашем компиляторе, обратитесь к справочному руководству по нему. Кроме того, можно получить любую информацию, которую компилятор знает о типе, например:

·         список функций-членов класса;

·         способ размещения объекта в памяти, т.е. взаимное расположение подобъектов базового и производных классов.

Одним из способов расширения поддержки RTTI является включение дополнительной информации в класс, производный от type_info. Поскольку в классе type_info

есть виртуальный деструктор, то оператор dynamic_cast позволяет выяснить, имеется ли некоторое конкретное расширение RTTI. Предположим, что некоторый компилятор предоставляет расширенную поддержку RTTI посредством класса extended_type_info, производного от type_info. С помощью оператора dynamic_cast

программа может узнать, принадлежит ли объект типа type_info, возвращенный оператором typeid, к типу extended_type_info. Если да, то пользоваться расширенной поддержкой RTTI разрешено.



#include <typeinfo>

// Файл typeinfo содержит определение типа extended_type_info

void func( employee* p )

{

   // понижающее приведение типа type_info* к extended_type_info*

   if ( eti *eti_p = dynamic_cast<eti *>( &typeid( *p ) ) )

   {

      // если dynamic_cast завершается успешно,

      // можно пользоваться информацией из extended_type_info через eti_p

   }

   else

   {

      // если dynamic_cast завершается неудачно,

      // можно пользоваться только стандартным type_info

   }
<


}

Если dynamic_cast

завершается успешно, то оператор typeid вернет объект класса extended_type_info, т.е. компилятор обеспечивает расширенную поддержку RTTI, чем программа может воспользоваться. В противном случае допустимы только базовые средства RTTI.

Упражнение 19.1

Дана иерархия классов, в которой у каждого класса есть конструктор по умолчанию и виртуальный деструктор:



class X { ... };

class A { ... };

class B : public A { ... };

class C : public B { ... };
class D : public X, public C { ... };

Какие из данных операторов dynamic_cast

завершатся неудачно?



(a) D *pd = new D;
    A *pa = dynamic_cast< A* > ( pd );



(b) A *pa = new C;

    C *pc = dynamic_cast< C* > ( pa );



(c) B *pb = new B;

    D *pd = dynamic_cast< D* > ( pb );



(d) A *pa = new D;

    X *px = dynamic_cast< X* > ( pa );

Упражнение 19.2

Объясните, когда нужно пользоваться оператором dynamic_cast вместо виртуальной функции?

Упражнение 19.3

Пользуясь иерархией классов из упражнения 19.1, перепишите следующий фрагмент так, чтобы в нем использовался ссылочный вариант dynamic_cast для преобразования *pa в тип D&:



if ( D *pd = dynamic_cast< D* >( pa ) ) {

   // использовать члены D

}

else {

   // использовать члены A
}

Упражнение 19.4

Дана иерархия классов, в которой у каждого класса есть конструктор по умолчанию и виртуальный деструктор:



class X { ... };

class A { ... };

class B : public A { ... };

class C : public B { ... };
class D : public X, public C { ... };

Какое имя типа будет напечатано в каждом из следующих случаев:



(a) A *pa = new D;

    cout << typeid( pa ).name() << endl;

(b) X *px = new D;

    cout << typeid( *px ).name() << endl;

(c) C obj;

    A& ra = cobj;

    cout << typeid( &ra ).name() << endl;

(d) X *px = new D;

    A& ra = *px;
    cout << typeid( ra ).name() << endl;


Содержание раздела