Применение оператора typeid к шаблонным классам



 

Оператор typeid можно применить и к шаблонным классам. Тип объекта, который является экземпляром шаблонного класса, определяется частично на основании того, какие именно данные используются для его обобщенных данных при реализации объекта. Таким образом, два экземпляра одного и того же шаблонного класса, которые создаются с использованием различных данных, имеют различный тип. Рассмотрим простой пример.

 

 

Эта программа генерирует такие результаты.

 

 

Как видите, несмотря на то, что два объекта являются экземплярами одного и того же шаблонного класса, если их параметризованные данные не совпадают, они не эквивалентны по типу. В этой программе объект o1 имеет тип myclass<int> , а объект o3 – тип myclass<double> . Таким образом, это объекты разного типа.

Рассмотрим еще один пример применения оператора typeid к шаблонным классам, а именно модифицированную версию программы определения геометрических фигур из предыдущего раздела. На этот раз класс figure мы сделали шаблонным.

 

 

Вот как выглядит возможный результат выполнения этой программы.

 

 

Динамическая идентификация типов используется не в каждой программе. Но при работе с полиморфными типами это средство позволяет узнать тип объекта, обрабатываемого в любой произвольный момент времени.

 

Операторы приведения типов

 

В C++ определено пять операторов приведения типов. Первый оператор (он описан выше в этой книге), применяемый в обычном (традиционном) стиле, был с самого начала встроен в C++. Остальные четыре (dynamic_cast , const_cast , reinterpret_cast и static_cast ) были добавлены в язык всего несколько лет назад. Эти операторы предоставляют дополнительные "рычаги управления" характером выполнения операций приведения типа. Рассмотрим каждый из них в отдельности.

 

Оператор dynamic_cast

 

Оператор dynamic_cast выполняет операцию приведения полиморфных типов во время выполнения программы.

Возможно, самым важным из новых операторов является оператор динамического приведения типов dynamic_cast . Во время выполнения программы он проверяет обоснованность предлагаемой операции. Если в момент его вызова заданная операция оказывается недопустимой, приведение типов не производится. Общий формат применения оператора dynamic_cast таков.

dynamic_cast<type> (expr)

Здесь элемент type означает новый тип, который является целью выполнения этой операции, а элемент expr – выражение, приводимое к этому новому типу. Тип type должен быть представлен указателем или ссылкой, а выражение expr должно приводиться к указателю или ссылке. Таким образом, оператор dynamic_cast можно использовать для преобразования указателя одного типа в указатель другого или ссылки одного типа в ссылку другого.

Этот оператор в основном используется для динамического выполнения операций приведения типа среди полиморфных типов. Например, если даны полиморфные классы В и D , причем класс D выведен из класса В , то с помощью оператора dynamic_cast всегда можно преобразовать указатель D* в указатель В* , поскольку указатель на базовый класс всегда можно использовать для указания на объект класса, выведенного из базового. Однако оператор dynamic_cast может преобразовать указатель В* в указатель D* только в том случае, если адресуемым объектом действительно является объект класса D . И, вообще, оператор dynamic_cast будет успешно выполнен только при условии, если разрешено полиморфное приведение типов, т.е. если указатель (или ссылка), приводимый к новому типу, может указывать (или ссылаться) на объект этого нового типа или объект, выведенный из него. В противном случае, т.е. если заданную операцию приведения типов выполнить нельзя, результат действия оператора dynamic_cast оценивается как нулевой, если в этой операции участвуют указатели. (Если же попытка выполнить эту операцию оказалась неудачной при участии в ней ссылок, генерируется исключение типа bad_cast .)

Рассмотрим простой пример. Предположим, что класс Base – полиморфный, а класс Derived выведен из класса Base .

 

 

Здесь приведение указателя bp (на базовый класс) к указателю dp (на производный класс) успешно выполняется, поскольку bp действительно указывает на объект класса Derived . Поэтому при выполнении этого фрагмента кода будет выведено сообщение Приведение типа прошло успешно! . Но в следующем фрагменте кода попытка совершить операцию приведения типа будет неудачной, поскольку bp  в действительности указывает на объект класса Base , и неправомерно приводить указатель на базовый класс к типу указателя на производный, если адресуемый им объект не является на самом деле объектом производного класса.

 

 

Поскольку попытка выполнить операцию приведения типа оказалась неудачной, при выполнении этого фрагмента кода будет выведено сообщение Приведение типа выполнить не удалось .

В следующей программе демонстрируются различные ситуации применения оператора dynamic_cast .

 

 

Программа генерирует такие результаты.

 

 

Оператор dynamic_cast можно иногда использовать вместо оператора typeid . Например, предположим, что класс Base – полиморфный и является базовым для класса Derived , тогда при выполнении следующего фрагмента кода указателю dp будет присвоен адрес объекта, адресуемого указателем bp , но только в том случае, если этот объект действительно является объектом класса Derived .

 

 

В этом случае используется обычная операция приведения типов. Здесь это вполне безопасно, поскольку инструкция if проверяет законность операции приведения типов с помощью оператора typeid до ее реального выполнения. То же самое можно сделать более эффективно, заменив операторы typeid и инструкцию if оператором

 

 

Поскольку оператор dynamic_cast успешно выполняется только в том случае, если объект, подвергаемый операции приведения к типу, уже является объектом либо заданного типа, либо типа, выведенного из заданного, то после завершения этой инструкции указатель dp будет содержать либо нулевое значение, либо указатель на объект типа Derived . Кроме того, поскольку оператор dynamic_cast успешно выполняется только в том случае, если заданная операция приведения типов законна, то в определенных ситуациях ее логику можно упростить. В следующей программе показано, как оператор typeid можно заменить оператором dynamic_cast . Здесь выполняется один и тот же набор операций дважды: с использованием сначала оператора typeid , а затем оператора dynamic_cast .

 

 

Как видите, использование оператора dynamic_cast упрощает логику, необходимую для преобразования указателя на базовый класс в указатель на производный класс. Вот как выглядят результаты выполнения этой программы.

 

 

И еще. Оператор dynamic_cast можно также использовать применительно к шаблонным классам.

 

Оператор const_cast

 

Оператор const_cast переопределяет модификаторы const и/или volatile.

Оператор const_cast используется для явного переопределения модификаторов const и/или volatile . Новый тип должен совпадать с исходным, за исключением его атрибутов const или volatile . Чаще всего оператор const_cast используется для удаления признака постоянства (атрибута const ). Его общий формат имеет следующий вид.

 

 

Здесь элемент type задает новый тип операции приведения, а элемент expr означает выражение, которое приводится к новому типу.

Использование оператора const_cast демонстрируется в следующей программе.

 

 

Результаты выполнения этой программы таковы.

 

 

Как видите, переменная x была модифицирована функцией f() , хотя параметр, принимаемый ею, задается как const ‑указатель.

Необходимо подчеркнуть, что использование оператора const_cast для удаления const ‑атрибута является потенциально опасным средством. Поэтому обращайтесь с ним очень осторожно.

И еще. Удалять const ‑атрибут способен только оператор const_cast . Другими словами, ни dynamic_cast , ни static_cast , ни reinterpret_cast нельзя использовать для изменения const ‑атрибута объекта.

 

Оператор static_cast

 

Оператор static_cast выполняет операцию неполиморфного приведения типов.

Оператор static_cast выполняет операцию неполиморфного приведения типов. Его можно использовать для любого стандартного преобразования. При этом во время работы программы никаких проверок на допустимость не выполняется. Оператор static_cast имеет следующий общий формат записи.

 

 

Здесь элемент type задает новый тип операции приведения, а элемент expr означает выражение, которое приводится к этому новому типу.

Оператор static_cast , по сути, является заменителем оригинального оператора приведения типов. Он лишь выполняет неполиморфное преобразование. Например, при выполнении следующей программы переменная типа float приводится к типу int .

 

Оператор reinterpret_cast

 

Оператор reinterpret_cast выполняет фундаментальное изменение типа.

Оператор reinterpret_cast преобразует один тип в принципиально другой. Например, его можно использовать для преобразования указателя в целое значение и целого значения – в указатель. Его также можно использовать для приведения наследственно несовместимых типов указателей. Этот оператор имеет следующий общий формат записи.

 

 

Здесь элемент type задает новый тип операции приведения, а элемент expr означает выражение, которое приводится к этому новому типу.

Использование оператора reinterpret_cast демонстрируется в следующей программе.

 

 

Здесь оператор reinterpret_cast преобразует указатель p в целочисленное значение. Данное преобразование представляет фундаментальное изменение типа.

 

Сравнение обычной операции приведения типов с новыми четырьмя cast‑операторами

 

Кому‑то из читателей могло бы показаться, что описанные выше четыре cast ‑оператора полностью заменяют традиционную операцию приведения типов. И тогда у них может возникнуть такой вопрос: "Стоит ли всегда вместо обычной операции приведения типов использовать более новые средства?" . Дело в том, что общего правила для всех программистов не существует. Поскольку новые операторы были созданы для повышения безопасности довольно рискованной операции приведения одного типа данных к другому, многие С++‑программисты убеждены, что их следует использовать исключительно с этой целью. И здесь трудно что‑либо возразить. Другие же программисты считают, что поскольку традиционная операция приведения типов служила им "верой и правдой" в течение многих лет, то от нее не стоит так легко отказываться. Например, для выполнения простых и относительно безопасных операций приведения типов (как те, что требуются при вызове функций ввода‑вывода read() и write() , описанных в предыдущей главе) "старое доброе" средство вполне приемлемо.

Существует еще одна точка зрения, с которой трудно не согласиться: при выполнении операций приведения полиморфных типов определенно стоит использовать оператор dynamic_cast .

 


Дата добавления: 2018-09-22; просмотров: 319; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!