Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lucik_op.doc
Скачиваний:
2
Добавлен:
01.03.2025
Размер:
3 Мб
Скачать
☆

Преобразование типа

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

На первом этапе выполняется попытка использовать стандартные преобразования типов. Если это невозможно, то компилятор использует преобразования, определенные пользователем.

Явные преобразования типов

Явное приведение типа выполненное в стиле языка С имеет вид:

(тип) выражение.

То есть если перед выражением указать имя типа в круглых скобках, то значение выражения будет преобразовано к данному типу. Например:

int a = (int) b;

char *s=(char *) addr;

Недостатком их является полное отсутствие контроля, что приводит к ошибкам и путанице. Существует большая разница между приведением указателя на const объект к указателю на не const объект (изменяется только атрибут объекта), и приведением указателя на объект базового класса к указателю на объект производного класса (изменяется полностью тип указателя). Было бы неплохо более точно определять цель каждого приведения. Чтобы преодолеть недостатки приведения типов выполняемых в стиле языка С, в язык С++ введены четыре оператора приведения типа: static_cast, const_cast, dynamic_cast и reinterpret_cast. Спецификация преобразования имеет следующий вид:

оператор_приведения _типа<тип> (выражение).

Для преобразования с минимальным контролем можно использовать оператор staic_cast. Он позволяет выполнять преобразования, не проверяя типы выражений во время выполнения, а основываясь на сведениях, полученных при компиляции. Оператор static_cast позволяет выполнять преобразования не только указателя на базовый класс к указателю на производный, но и наоборот.

int i,j;

double d=static_cast<double>(i)*j;

В то же время, например, преобразование int к int* (и обратно) приведет к ошибке компиляции. Рассматриваемые далее операторы приведения используются для более узкого круга задач. Для преобразований не связанных между собой типов используется reinterpret_cast.

int i;

void *adr= reinterpret_cast<void *> (i);

Необходимо отметить, что reinterpret_cast не выполнит преобразование таких типов как float, double, объектов к типу указателя.

Если же нужно выполнить преобразование выражения имеющего тип с атрибутами char и volatile к типу без них, то можно использовать оператор const_cast:

const char *s;

char *ss=const_cast<char*> (s);

Следующий оператор dynamic_cast предназначен для приведения указателя или ссылки на объект базового класса к указателям или ссылкам на объекты производных классов. При этом можно определить, была ли попытка приведения типа успешной. В результате неудачной попытки преобразования возвращается либо нулевой указатель, либо возникает исключение (при преобразовании ссылок).

class A { . . . };

class B : public A { . . .};

class C : public B { . . .};

main()

{ A *pa;

B *pb;

C *pc;

. . .

pa= dynamic_cast<A*>(pc);

pb= dynamic_cast<B*>(pc);

}

dynamic_cast Operator

The expression dynamic_cast<type-id>( expression ) converts the operand expression to an object of type type-id. The type-id must be a pointer or a reference to a previously defined class type or a “pointer to void”. The type of expression must be a pointer if type-id is a pointer, or an l-value if type-id is a reference.

Syntax

dynamic_cast < type-id > ( expression )

If type-id is a

pointer to an unambiguous accessible direct or indirect base class of expression, a pointer to the unique subobject of type type-id is the result. For example:

class B { ... };

class C : public B { ... };

class D : public C { ... };

void f(D* pd)

{

C* pc = dynamic_cast<C*>(pd); // ok: C is a direct base class

// pc points to C subobject of pd

B* pb = dynamic_cast<B*>(pd); // ok: B is an indirect base class

// pb points to B subobject of pd

...

}

This type of conversion is called an “upcast” because it moves a pointer up a class hierarchy, from a derived class to a class it is derived from. An upcast is an implicit conversion.

If type-id is void*, a run-time check is made to determine the actual type of expression. The result is a pointer to the complete object pointed to by expression. For example:

class A { ... };

class B { ... };

void f()

{

A* pa = new A;

B* pb = new B;

void* pv = dynamic_cast<void*>(pa);

// pv now points to an object of type A

...

pv = dynamic_cast<void*>(pb);

// pv now points to an object of type B

}

If type-id is not void*, a run-time check is made to see if the object pointed to by expression can be converted to the type pointed to by type-id.

If the type of expression is a base class of the type of type-id, a run-time check is made to see if expression actually points to a complete object of the type of type-id. If this is true, the result is a pointer to a complete object of the type of type-id. For example:

class B { ... };

class D : public B { ... };

void f()

{

B* pb = new D; // unclear but ok

B* pb2 = new B;

D* pd = dynamic_cast<D*>(pb); // ok: pb actually points to a D

...

D* pd2 = dynamic_cast<D*>(pb2); //error: pb2 points to a B, not a D

// pd2 == NULL

...

}

This type of conversion is called a “downcast” because it moves a pointer down a class hierarchy, from a given class to a class derived from it.

In cases of multiple inheritance, possibilities for ambiguity are introduced. Consider the class hierarchy shown in Figure 4.5:

Figure 4.5   Class Hierarchy Showing Multiple Inheritance

A pointer to an object of type D can be safely cast to B or C. However, if D is cast to point to an A object, which instance of A would result? This would result in an ambiguous casting error. To get around this problem, you can perform two unambiguous casts. For example:

void f()

{

D* pd = new D;

A* pa = dynamic_cast<A*>(pd); // error: ambiguous

B* pb = dynamic_cast<B*>(pd); // first cast to B

A* pa2 = dynamic_cast<A*>(pb); // ok: unambiguous

}

Further ambiguities can be introduced when you use virtual base classes. Consider the class hierarchy shown in Figure 4.6:

Figure 4.6   Class Hierarchy Showing Virtual Base Classes

In this hierarchy, A is a virtual base class. See Virtual Base Classes for the definition of a virtual base class. Given an instance of class E and a pointer to the A subobject, a dynamic_cast to a pointer to B will fail due to ambiguity. You must first cast back to the complete E object, then work your way back up the hierarchy, in an unambiguous manner, to reach the correct B object.

Consider the class hierarchy shown in Figure 4.7:

Figure 4.7   Class Hierarchy Showing Duplicate Base Classes

Given an object of type E and a pointer to the D subobject, to navigate from the D subobject to the left-most A subobject, three conversions can be made. You can perform a dynamic_cast conversion from the D pointer to an E pointer, then a conversion (either dynamic_cast or an implicit conversion) from E to B, and finally an implicit conversion from B to A. For example:

void f(D* pd)

{

E* pe = dynamic_cast<E*>(pd);

B* pb = pe; // upcast, implicit conversion

A* pa = pb; // upcast, implicit conversion

}

The dynamic_cast operator can also be used to perform a “cross cast.” Using the same class hierarchy, it is possible to cast a pointer, for example, from the B subobject to the D subobject, as long as the complete object is of type E.

Considering cross casts, it is actually possible to do the conversion from a pointer to D to a pointer to the left-most A subobject in just two steps. You can perform a cross cast from D to B, then an implicit conversion from B to A. For example:

void f(D* pd)

{

B* pb = dynamic_cast<B*>(pd); // cross cast

A* pa = pb; // upcast, implicit conversion

}

A null pointer value is converted to the null pointer value of the destination type by dynamic_cast.

When you use dynamic_cast < type-id > ( expression ), if expression cannot be safely converted to type type-id, the run-time check causes the cast to fail. For example:

class A { ... };

class B { ... };

void f()

{

A* pa = new A;

B* pb = dynamic_cast<B*>(pa); // fails, not safe;

// B not derived from A

...

}

The value of a failed cast to pointer type is the null pointer. A failed cast to reference type throws a bad_cast exception.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]