|
Программирование >> Полиморфизм без виртуальных функций в с++
Разумеется, такое приведение можно выполнить лишь в том случае, если тип производного класса определяется однозначно. 14.2.3. Правильное и неправильное использование RTTI Пользоваться механизмом явной идентификации типа во время исполнения стоит только при необходимости. Статическая проверка (во вре.мя компиляции) безопаснее, связана с меньшими затратами и в тех случаях, когда ее хватает, позволяет получать лучше структурированные программы. Например, RTTI можно использовать для написания явно видимых операторов переключения: неправильное применение информации о типе: void rotate(const Shapes г) { if (typeid(r) == typeid(Circle)) { ничего не делать else if (typeid(r) == typeid(Triangle)) { повернуть треугольник else if (typeid(r) == typeid(Square)) { повернуть квадрат Я слышал, будто о таком стиле говорили, что он соединяет синтаксическое изящество С-1-1- с эффективностью Smalltalk, но это излишняя любезность. К сожалению, с помощью этого кода нельзя корректно обработать классы, производные от встречающихся в нем, следовательно, при добавлении в программу нового класса его придется модифицировать. void g{D& d) { В таком случае, лучше пользоваться виртуальными функциями. Мой опыт работы с подобным кодом в Simula и послужил той причиной, по KOTopoii в С++ первоначально ие были включены средства для идентификации типа во время исполнения (см. раздел 3.5). У многих пользователей, имеюших опыт работы с такими языками, как С, Pascal, Modula-2, Ada и т.д., возникает почти непреодоли.мое желание организовать код в виде набора предложешш switch. Как правило, этого делать ие следует. Заметьте, пожалуйста, что, хоть комитет 1ю стандартизации и одобрил механизм RTTI в С++, он реализован не в виде переключения по типу (как предложение INSPECT в Simula). Видимо, не стоит напрямую поддерживать такую .модель организации программы. Примеров, где это оправдано, гораздо меньше, че.м поначалу кажется многим программистам. Потом же оказывается, что для реорганизации нужно приложить слишком много усилий. Зачастую RTTI используется правильно, когда некий сервисный код выражен в терминах одного класса, а пользователь намерен расширить его функциональность с помощью наследования. Примеро.м может служить класс dialog box из раздела 14.2.1. Если пользователь хочет и может .модифицировать определения библиотечных классов, скажем, того же dialog box, то к RTTI можно и не прибегать, в ином случае это средство потребуется обязательно. Но даже если пользователь готов модифицировать базовые классы, на этом пути могут возникнуть определенные трудности. Например, может оказаться необходи.мы.м ввести заглушки в.место некоторых виртуальных функций, например get string () в классах, для которых такие функции не нужны или бессмысленны. Данная проблема обсуждается в книге [2nd, §13.13.6] под заголовко.м Fat Interfaces* ( толстые интерфейсы). О применении RTTI для реализации простой систе.мы ввода/вывода объектов говорится в разделе 14.2.7. У людей, ранее работавших с языками, где интенсивно используется дина.ми-ческая проверка типов (например, Smalltalk), возникает желание при.менить RTTI в сочетании с излишне обобщенными типами. Напри.мер: неправильное применение информации о типе: class Object { /* ... */ }; class Container : public Object { public: void put(Object*); Object* get(); . . . class Ship : public Object { /* ... */ }; Ship* f(Ship* ps. Container* c) c->put(ps); . . . Object* р = c->get(); if (Ship* q = dynamic cast<Ship*>(p)) проверка во время выполнения return q; сделать что-то еще (обычно - обработать ошибку) Здесь класс Obj ect совершенно не нужен. Он слишком обший, так как не соответствует ни одному из понятий предметной области и забавляет програм.миста оперировать на более низком уровне абстракции, чем необходимо. Такого рода задачи лучше решать с помошью шаблонов контейнеров, содержащих указатели только одного типа: template<class Т> class Container.{ public: void put(T*); T* getO; . . . Ship* fp(Ship* ps, Container<Ship>* c) { C->put(ps); ... return c->get(); В сочетании с виртуальными функциями этот прием подходит в большинстве случаев. 14.2.4. Зачем давать опасные средства Итак, если я точно предвидел, что RTTI будут использовать ненадлежащим образом, то зачем вообще спроектировал этот механизм и боролся за его одобрение? Хорошие программы - это продукт хорошего образования, удачного проектирования, адекватного тестирования и т.д., а не наличия в языке средств, которые предположительно должны использоваться только правильно . Употребить во вред можно любое средство, поэтому вопрос не в том, можно ли чем-то воспользоваться неправильно (можно!) или будут ли эти.м неправильно пользоваться (будут!). Вопрос в том, необходимо ли данное средство при условии его правильного употребления настолько, чтобы оправдать затраты на его реализацию, удастся ли смоделировать его с помощью других средств языка и реально ли с помощью обучения обратить во благо неправильное использование. Размышляя некоторое время над RTTI, я пришел к выводу, что перед нами обычная проблема стандартизации: □ в большинстве крупных библиотек RTTI поддерживается; □ как правило, средство предоставляется в тако.м виде, который требует от пользователя написания громоздкого дополнительного кода, в результате чего весьма вероятны ошибки;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |