|
Программирование >> Разработка устойчивых систем
Компилятор обычно вставляет указатель на таблицу RTTI класса в таблицу виртуальных функций. portfolio.push back(new Stock): for (vector<Security*>::iterator it = portfolio.begin(): it !- portfolio.endO: ++it) { Investment* cm - dynamic cast<Investment*>(*it): if(cm) cm->special0: else cout not a Investment endl: cout cast from intermediate pointer: endl: Security* sp - new Metal: Investment* cp - dynamic cast<Investment*>(sp): if(cp) cout its an Investment endl: Metal* mp - dynamic cast<Metal*>(sp): if(mp) cout its a Metal too! endl: purge(portfolio): } III:- Эта программа гораздо короче, потому что большая часть кода исходного примера содержала избыточные проверки преобразований. Целевой тип dynamic cast указывается в угловых скобках, как и для остальных новых операторов преобразований типов С++ (static cast и т. д.), а преобразуемый объект передается в виде операнда. Чтобы понижающие преобразования были безопасными, оператор dynamic cast требует, чтобы используемые типы были полиморфными то есть содержали как минимум одну виртуальную функцию. К счастью, в базовом классе Security имеется виртуальный деструктор, поэтому нам не придется вставлять лишнюю функцию. Так как оператор dynamic cast работает на стадии выполнения, используя таблицу виртуальных функций, он обычно требует больших издержек, чем остальные новые операторы преобразования типов. Оператор dynamic cast также может применяться к ссылкам вместо указателей, но так как нулевых ссылок не существует, информация о неудачном преобразовании должна передаваться другим способом. Этот другой способ основан на перехвате исключения bad cast: : C08:CatchBadCast.cpp #include <typeinfo> #include Security.h using namespace std: int mainO { Metal m: SecurityS s = m: try { Investments с = dynamic cast<Investment&>(s): cout Its an Investment endl: } catch (bad cast&) { cout s is not an Investment type endl: try { Bond& с = dynamic cast<Bond&>(s): cout Its an Bond endl: } catch (bad cast&) { cout Its not a Bond type endl: } /:- Оператор typeid Другой способ получения динамической информации об объекте основан на применении оператора typeid. Этот оператор возвращает объект класса typejnfo с информацией о типе объекта, к которому он был применен. Для полиморфных типов возвращается информация о наиболее производном (динамическом) типе, а в остальных случаях - статическая информация о типе. В частности, оператор typeid может использоваться для получения динамического имени объекта в формате const char*, как показано в следующем примере: : С08:ТуреInfo.срр Применение оператора typeid #i nclude <iostream> #include <typeinfo> using namespace std: struct PolyBase {virtual ~PolyBase(){}}: struct PolyDer : PolyBase { PolyDerO {} }; struct NonPolyBase {}: struct NonPolyDer : NonPolyBase {NonPolyDer(int){} }: int mainO { Для полиморфных типов const PolyDer pd: const PolyBase* ppb = &pd: cout typeid(ppb).name() endl: cout typeid(*ppb).nameO endl: cout boolalpha (typeid(*ppb) == typeid(pd)) endl: cout (typeid(PolyDer) == typeid(const PolyDer)) endl: Для неполиморфных типов const NonPolyDer npd(l): const NonPolyBase* nppb = &npd: cout typeid(nppb).nameО endl: cout typeid(*nppb).name() endl: cout (typeid(*nppb) == typeid(npd)) endl: Для встроенных типов int i: cout typeid(i).name() endl: } /:- Результат выполнения программы для одного конкретного компилятора выглядит так: struct PolyBase const * struct PolyDer true true struct NonPolyBase const * struct NonPolyBase false int Класс bad cast определяется в заголовке <typeinfo>. Как и большая часть стандартной библиотеки, он принадлежит к пространству имен std. Первая строка просто воспроизводит статический тип ррЬ. Чтобы механизм RTTI вступил в силу, следует разыменовать указатель, как это сделано во второй строке. Обратите внимание: RTTI игнорирует квалификаторы const и volatile верхнего уровня. Для неполиморфных типов возвращается статический тип (то есть тип самого указателя). Как видите, RTTI также работает с встроенными типами. Оказывается, результат выполнения оператора typeid невозможно сохранить в объекте typejnfo, так как конструкторы для него недоступны, а присваивание запрещено. Информация должна использоваться так, как показано в примере. Кроме того, конкретный вид строки, возвращаемой функцией type info::name(), зависит от компилятора. Например, для класса с именем С некоторые компиляторы возвращают строку class С вместо С. Применение оператора typeid к выражению, разыменовывающему нулевой указатель, приводит к запуску исключения bad typeid (также определяемому в <typeinfo>). Следующий пример показывает, что для вложенных классов вызов type info::name() возвращает полностью уточненное имя: : COSiRTTIandNesting.cpp linclude <iostream> linclude <typeinfo> using namespace std: class One { class Nested {}: Nested* n: public: OneO : n(new Nested) {} ~One() { delete n: } Nested* nestedО { return n: } int mainO { One 0: cout typeid(*o.nested()).nameO endl: } /:- Поскольку Nested является вложенным типом по отношению к классу One, в результате выводится строка One::Nested. Можно выяснить, предшествует ли объект typejnfo другому объекту typejnfo в некотором порядке, определяемом реализацией. Для этой цели используется функция before (type Jnfo&), возвращающая true или false. Так, следующая строка проверяет, предшествует ли тип те типу you в текущем порядке: if(typeid(me).before(typeid(you))) ... Такая возможность может пригодиться при использовании объектов typejnfo в качестве ключей. Преобразование к промежуточным типам Как было показано в предыдущей программе с иерархией классов Security, оператор dynamic cast способен обнаруживать как фактический тип, так и промежуточные типы в многоуровневых иерархиях. Пример: : C08:IntermediateCast.cpp linclude <cassert>
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.074
При копировании материалов приветствуются ссылки. |