|
Программирование >> Разработка устойчивых систем
sumValue(glassB1n. cout): sum\/alue(bin. cout): purge(bin): } III:- Сначала несортированный мусор бросается в один общий бак, так что специализированная информация типа теряется . Но позднее для правильной сортировки необходимо получить фактическую информацию о типе, поэтому мы используем RTTI. Данное рещение можно улучшить за счет отображения, связывающего указатели на объекты typejnfo с вектором указателей на Trash. Для отображения необходим предикат упорядочения, поэтому мы предоставляем такой предикат с именем TInfoLess, вызывающий функцию typeJnfo::before(). В процессе вставки в отображение указатели на Trash автоматически ассоциируются с их ключами typejnfo. Обратите внимание на изменение определения sumValueOB новом варианте решения: : C08:Recycle2.cpp {L} Trash Переработка мусора с использованием отображения #1 ncl ude <cstcllib> #include <ctime> #include <iostream> #i nclude <map> #1 nclude <typeinfo> linclude <utility> linclude <vector> linclude Trash.h linclude ../purge.h using namespace std: Критерий сравнения указателей на type info struct TInfoLess { bool operatorO (const typejnfo* tl. const typejnfo* t2) const { return tl->before(*t2): } typedef map<const type info*. vector<Trash*>. TInfoLess> TrashMap: Вычисление суммарной стоимости одного вида мусора: void sumValue(const TrashMap::value type& p. ostream& os) { vector<Trash*>:: const Jterator tally = p. second, begi n(): float val =0: while(tally != p.second.endO) { val += (*tally)->weight() * (*tally)->value(): OS weight of p.first->name() typejnfo:: name О = (*ta11y)->weight() endl: ++tally: OS Total value = val endl: int mainO { srand(time(0)): Раскрутка генератора случайных чисел TrashMap bin: Реализация и издержки RTTI Обычно реализация RTTI базируется на включении дополнительного указателя в таблицу виртуальных функций класса. Этот указатель ссылается на структуру typejnfo для этого конкретного типа. Выражение typeid() реализуется достаточно просто: по указателю на таблицу виртуальных функций производится выборка указателя на typejnfo, через который осуществляется доступ к структуре typejnfo. Поскольку операция сводится к разыменованию двух указателей, она выполняется с постоянной сложностью. Выражение 6упатк са$1<приемник*>{указатель на источник) в больщинстве случаев также реализуется достаточно прямолинейно: сначала производится выборка информации RTTI для типов указателей на источник и приемник. Затем библиотечная функция проверяет, относится ли тип указателя на источник к типу приемник* или к типу его базового класса. Возвращаемый ею указатель может быть модифицирован при множественном наследовании, если базовый тип не является первым базовым типом производного класса. Множественное наследование порождает и другие трудности из-за возможности многократного вхождения базовых типов в иерархию и использования виртуальных базовых классов. Поскольку библиотечная функция в реализации dynamic cast должна проверить список базовых классов, затраты на вызов dynamic cast могут превышать затраты на вызов typeid (с другой стороны, вы получаете другую информацию, которая может оказаться критически важной для вашего решения), а на идентификацию базового iCTiacca может уйти больше времени, чем на идентификацию производного Заполнение контейнера обьектами Trash: forCint i = 0: i < 30: i++) { Trash* tp: switchCrandO % 3) { case 0 : tp = new Aluminum((rand() % 1000)/10.0): break: case 1 : tp = new Paper((rand() % 1000)/10.0): break: case 2 : tp = new Glass((rand() % 1000)/10.0): break: bi n[&typei d(*tp)].push back(tp): Вывод отсортированных результатов forCTrashMap::iterator p = bin.beginO: p != bin.endO: ++p) { sum\/alue(*p. cout): purge(p->second): } /:- Теперь sumValueO вызывает функцию typeJnfo::name() напрямую, потому что объект typejnfo стал доступен как первый компонент пары Trash Map: :value type. Тем самым предотвращается лищний вызов оператора typeid для получения имени типа Trash, необходимый в предыдущей версии программы. Упражнения 1. Создайте класс Base с виртуальным деструктором и производный от него класс Derived. Создайте вектор указателей на Base, которые случайным образом распределяются между объектами Base и Derived. Используя содержимое этого вектора, заполните второй вектор всеми указателями на Derived. Сравните время выполнения с операторами typeid и dynamic cast и посмотрите, какой вариант работает быстрее. 2. Измените класс C16:AutoCounter.h из первого тома так, чтобы он стал действительно полезным средством отладки. Он должен использоваться в качестве вложенного члена каждого класса, который вы планируете трассировать. Оформите AutoCounter в виде шаблона, параметризованного по имени внешнего класса. Во всех сообщениях об ошибках выводите имя класса средствами RTTI. 3. Используйте RTTI для вывода отладочной информации (полного имени специализированного шаблона) функцией typeid(). Специализируйте шаблон для разных типов и проанализируйте результаты. 4. Измените иерархию Instrument из главы 14 первого тома, но для начала скопируйте файл Winds.срр в другое место. Включите в класс Wind виртуальную функцию clearSpitValve() и переопределите ее для всех классов, производных от Wind. Создайте вектор указателей на Instrument; заполните его различными типами объектов Instrument, созданными оператором new. Далее при переборе элементов средствами RTTI выявляйте объекты класса Wind или производных от него. Вызовите для этих объектов функцию clearSpitValve(). Обратите внимание, что присутствие функции clearSpitValve() в базовом классе привело бы к раздражающему и ненужному усложнению интерфейса. класса. Кроме того, dynamic cast позволяет сравнивать любую пару типов, не ограничиваясь сравнением типов, принадлежащих к одной иерархии. Это также усложняет работу библиотечной функции, используемой оператором dynamic cast. Итоги Хотя в общем случае указатели повышаются до базового класса с последующим использованием интерфейса базового класса (через виртуальные функции), иногда наличие информации о динамическом типе объекта, на который ссылается указатель на базовый класс, позволило бы повысить эффективность программы. Именно эту возможность вам предоставляет RTTI. Чаще всего ею злоупотребляют программисты, которые не понимают принципов работы виртуальных функций и применяют RTTI для ручного кодирования проверки типов. С++ предоставляет в ваше распоряжение мощный инструментарий и защиту от нарушений целостности типов, но если вы сознательно хотите неправильно или нетривиально задействовать какой-нибудь аспект языка, ничто не помешает вам в этом. Что ж, мы быстрее всего учимся на собственных ошибках.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |