На этом шаге мы рассмотрим использование новых операторов преобразования типа.
Чтобы программисты могли более четко выразить смысл явного преобразования типа с одним аргументом, в стандарт были включены четыре новых оператора.
Оператор преобразует значение на логическом уровне. Считайте, что он создает временный объект, который затем инициализируется преобразуемыми данными. Преобразование разрешено только при наличии преобразования типа, определенного либо по умолчанию, либо явно. Пример:
float x; . . . . cout << static_cast<int>(x); // Вывод х в формате int . . . . f(static_cast<string>("hello")); // Функция f() вызывается для string. // а не для char*
Оператор позволяет привести полиморфный тип к "настоящему" статическому типу. Это единственное преобразование типа, которое проверяется на стадии выполнения программы; следовательно, оно может использоваться для проверки типов полиморфных значений. Пример:
class Car: // Абстрактный базовый класс // (содержит хотя бы одну чисто виртуальную функцию) class Cabriolet : public Car { . . . . }; class Limousine : public Car { . . . . }; void f(Car* cp) { Cabriolet* p = dynamic_cast<Cabriolet*>(cp); if (p == NULL) { // p не указывает на объект типа Cabriolet . . . . } }
В данном примере в функции f() определены специальные действия для тех объектов, которые не относятся к "настоящему" статическому типу Cabriolet. Если аргумент является ссылкой, а преобразование типа завершается неудачей, dynamic_cast генерирует исключение bad_cast. Помните, что с точки зрения проектирования при работе с полиморфными типами всегда лучше избегать выбора действий в зависимости от фактического типа.
Оператор создает или отменяет атрибут "константности" типа. Кроме того, допускается отмена атрибута volatile. Все остальные модификации типа не разрешаются.
Поведение этого оператора определяется реализацией. Он может (хотя и не обязан) повторно интерпретировать биты, из которых состоит величина. Обычно применение этого преобразования затрудняет переносимость программы.
Перечисленные операторы заменяют старую методику преобразования типов с использованием круглых скобок. К преимуществам нового способа можно отнести то, что он более наглядно выражает намерения программиста при преобразовании. Старый синтаксис применялся во всех рассмотренных случаях, кроме dynamic_cast, поэтому, встретив этот оператор в программе, трудно было сразу понять смысл преобразования. Новые операторы также снабжают компилятор дополнительной информацией о причинах преобразования и помогают выявлять ошибки, если преобразование выходит за границы положенного.
Обратите внимание - новые операторы преобразования типа определены только для одного аргумента. Рассмотрим следующий пример:
static_cast<Fraction>(15,100) // Ошибка: создается Fraction (100)
Команда работает совсем не так, как можно было бы ожидать. Вместо того чтобы инициализировать временный объект дроби с числителем 15 и знаменателем 100, она инициализирует временный объект с единственным значением 100. Запятая в данном случае не является разделителем аргументов, а интерпретируется как оператор, который объединяет два выражения и возвращает второе из них. Правильным способом "преобразования" величин 15 и 100 в дробь по-прежнему остается следующая команда:
Fraction(15,100) // Правильно, создает Fraction(15,100)
На следующем шаге мы рассмотрим инициализацию константных статических членов класса.