Г. Шилтд - Самоучитель C++ (PDF) (1114887), страница 53
Текст из файла (страница 53)
Этим объектом, так же как и в случае с указателем, можетбыть объект производного класса. Когда оператор typeid применяют к неполиморфному классу, получают указатель или ссылку базового типа.Ниже представлена вторая форма оператора typeid, в которой в качестве аргумента указывают имя типа:typeid (имя_тйпа)Обычно с помощью данной формы оператора typeid получают объект типаtype_info, который можно использовать в инструкции сравнения типов.Поскольку оператор typeid чаще всего применяют к разыменованному указателю (т. е. указателю, к которому уже был применен оператор *), для обработки положения, когда разыменованный указатель равен нулю, былапридумана специальная исключительная ситуация bad_typeid, которую вэтом случае возбуждает оператор typeid.Динамическая идентификация типа используется далеко не в каждой программе.
Тем не менее, если вы работаете с полиморфными типами данных,она позволяет в самых разнообразных ситуациях определять типы обрабатываемых объектов.В следующей программе демонстрируется использование оператора typeid.Сначала с помощью этого оператора мы получаем информацию об одном извстроенных типов данных C++ — типе int. Затем оператор typeid дает намвозможность вывести на экран типы объектов, на которые указывает указатель р, являющийся указателем базового класса BaseClass.// Пример использования оператора typeid#include <iostream>^include <typeinfo>using namespace std;360___Самоучитель C++class BaseClass {virtual void f() (}; // делаем класс BaseClass полиморфнымclass Derivedl: public BaseClass {class Derived2 : public BaseClass {int main ( )iint i;BaseClass *p, baaeob;Derivedl obi;Derived2 ob2;// Вывод на экран встроенного типа данныхcout « "Тип переменной i — это ";cout « typeid(i) .name () « endl;-// Обработка полиморфных типовр = Sbaseob;cout « "Указатель р указывает на объект типа ";cout « typeid (*p) .name () « endl;р = &obl;cout « "Указатель р указывает на объект типа ";cout « typeid (*p) .name {) « endl;.р = &ob2;cout « "Указатель р указывает на объект типа ";cout « typeid{*p) .name (} « endl;return 0;Программа выводит на экран следующую информацию:Тип переменной i — это intУказатель р указывает на объект типа BaseClassУказатель р указывает на объект типа DerivedlУказатель р указывает на объект типа Derived2Как уже отмечалось, когда в качестве аргумента оператора typeid задан указатель полиморфного базового класса, реальный тип объекта, на который указывает указатель определяется во время выполнения программы, чтоочевидно по выводимой на экран информации.
В качестве эксперимента за-Глава12.Динамическаяидентификацияиприведениетипов_361комментируйте виртуальную функцию f() в определении базового классаBaseClass и посмотрите, что получится.2. Ранее уже говорилось, что когда в качестве аргумента оператора typeid указана ссылка полиморфного базового класса, возвращаемым типом будет типреального объекта, на который дана ссылка. Чаще всего это свойство используется в ситуациях, когда объекты передаются функциям по ссылке.
Например, в следующей программе в объявлении функции WhatType() объекттипа BaseClass задан параметром-ссылкой. Это означает, что функции WhatТуре() можно передавать ссылки на объекты типа BaseClass или типов, производных от класса BaseClass. Если в операторе typeid задать такой параметр,то он возвратит тип реального объекта.// Использование оператора typeid со ссылкой в качестве аргумента^include <iostream>^include <typeinfo>using namespace std;class BaseClass {virtual void f(){}; // делаем класс BaseClass полиморфнымclass Derivedl: public BaseClass {class Derived2: public BaseClass {// Задание ссылки в качестве параметра функцииvoid WhatType (BaseClass &ob){cout « "ob — это ссылка на объект типа ";cout « typeid(ob) ,name{) « endl;int main()int i;BaseClass baseob;Derivedl obi ;Derived2 ob2;WhatType (baseob)WhatType (obi)WhatType (ob2)362__СамоучительC++Программа выводит на экран следующую информацию:ob — это ссылка на объект типа BaseClassob — это ссылка на объект типа Derived!ob — это ссылка на объект типа Derived23.
Хотя получение имени типа объекта в некоторых ситуациях оказываетсявесьма полезным, часто бывает необходимо узнать, соответствуют ли другдругу типы нескольких объектов. Это легко сделать, зная что объект типаtype_info, возвращаемый оператором typeid, перегружает операторы == и !— ,В представленной ниже программе показано использование этих операторов.// Использование операторов == и != с оператором typeid^include <iostream>^include <typeinfo>using namespace std;class X {virtual void f() {}class Y {virtual void f () { }int main(){X xl, x2;Y yl;if (typeid(xl) == (typeid(x2))cout « "Тип объектов xl и х2 одинаковХп";elsecout « "Тип объектов xl и х2 не одинаков \п";if (typeid(xl) != ( typeid (yl))cout « "Тип объектов xl и yl не одинаков \п";elsecout « "Тип объектов xl и yl одинаков\п";return 0;Программа выводит на экран следующую информацию:Тип объектов xl и х2 одинаковТип объектов xl и yl не одинаков4.
Хотя в предыдущих примерах и были показаны некоторые приемы работы соператором type_info, главных его достоинств мы не увидели, поскольку типы объектов были известны уже на этапе компиляции программы. В еле-Глава 12. Динамическая идентификация и приведение типов363дующем примере этот пробел восполнен. В программе определена простаяиерархия классов, предназначенных для рисования на экране разного родагеометрических фигур. На вершине иерархии находится абстрактный классShape. Его наследуют четыре класса: Line, Square, Rectangle и NulIShapeФункция generator() генерирует объект и возвращает указатель на него.(Функцию, предназначенную для создания объектов, иногда называютфабрикой объектов.) То, какой именно объект создается, определяет генератор случайных чисел rand().
В функции main{) реализован вывод получающихся объектов разных типов на экран, исключая объекты типа NulIShape, укоторых нет какой бы то ни было формы. Поскольку объекты возникаютслучайно, заранее неизвестно, какой объект будет создан следующим. Следовательно, для определения типа создаваемых объектов требуется динамическая идентификация типа.// Использование операторов — и != с оператором typeid#include <iostream>tinclude <cstdlib>#include <typeinfo>using namespace std;class Shape (public:virtual void example(} = 0;class Rectangle: public Shape {public:void example (} {cout « "*****\ n **\n**\ n *****\ n » ;class Triangle: public Shape {public:void example ( } {cout « "*\n* *\n* *\n*****\n»;class Line: public Shape (public:void example () {cout « "*****\n";class NulIShape: public Shape {public:364_Самоучительvoid example () {I// Фабрика производных от класса Shape объектовShape *generator{)switch(rand(} % 4) {.case 0:returncase 1 ;returncase 2;returncase 3:returnnew Line;new Rectangle;new Triangle/new NullShape;1return NULL;int main ( ){int i;Shape *p;for(i=0; ip =* generator (}; // создание следующего объектаcout « typeid(*p) .name () « endl;// рисует объект, если он не типа NullShapeif (typeid(*p) != typeid (NullShape) )p->example () ;}return 0;Программа выводит на экран следующее:class Rectangleclass NullShapeclass TriangleC++Глава 12.
Динамическая идентификация и приведение типов365class Line*****class Rectangleclass Line*****class Triangle**•*•*•*class Triangle*****class Triangleclass Line5. Оператор typeid может работать с классами-шаблонами. Например, рассмотрим следующую программу. В ней для хранения некоторых значений создается иерархия классов-шаблонов. Виртуальная функция get_val() возвращаетопределенное в каждом классе значение. Для класса Num это значение соответствует самому числу. Для класса Square — это квадрат числа. Для классаSqr_root — это квадратный корень числа. Объекты, производные от классаNum, генерирует функция genegator(). С помощью оператора typeid определяется тип генерируемых объектов.// Использование оператора typeid с шаблонами^include <iostream>linclude <cstdlib>ttinclude <cmath#include <typeinfo>using namespace std;template <class T> class Num {public:Т х;Num(T i) { x = i; }366_Самоучительvirtual Т get_val() { return x; }};template <class T>class Squary: public Num<T> {public:Squary (T 1} : Num<T>(i) {}Т get_val() { return x*x; }•template <class T>class Sqr_root: public Num<T> {public:Sqr_root(T i): Num<T>(i) {}Т get_val() { return sqrt ( (double) x) ; });// Фабрика производных от класса Num объектовNum<double> * generator {)(switch {rand () % 2) {case 0: return new Squary<double> (rand{) % 100);case 1: return new Sqr_root<double> (rand() % 100);}return NULL;int main(){Num<double> obi (10), *pl;Squary<double> ob2(100.0);Sqr_root<double> ob3{999.2);int i;cout « typeid (obi) .name () « endl;cout « typeid (ob2) .name () « endl;cout « typeid (ob3) .name () « endl;if (typeid(ob2) == typeid (Squary<double>) )cout « "is Squary<double>\n";pi = fiob2;if (typeid{*pl) != typeid(obl))cout « "Значение равно: " « pl->get__val (} ;cout « "\n\n";cout « "Теперь генерируем объекты\п";for(i=0; i<10; i++) {pi = generator{); // получение следующего объектаC++Глава 12.
Динамическая идентификация и приведение типов367if(typeid(*pl) == typeid CSquary<double>))cout « "Квадрат объекта: ";if (typeid(*pl) == typeid(Sqr__root<double>) )cout « "Квадратный корень объекта: ";cout « "Значение равно: " « pl->get_val()cout « endl;return 0;Программа выводит на экран следующее:class Num<double>class Squary<double>class Sqr_root<double>is Squary<double>Значение равно: 10000Теперь генерируем объектыКвадратный корень объекта : Значение равно :Квадрат объекта: Значение равно : 0Квадратный корень объекта : Значение равно:Квадрат объекта: Значение равно: 3364Квадрат объекта: Значение равно: 4096Квадратный корень объекта : Значение равно:Квадратный корень объекта : Значение равно:Квадратный корень объекта : Значение равно :Квадратный корень объекта: Значение равно :Квадратный корень объекта : Значение равно:8.