C++严格地限制允许的类型转换,并添加了4个类型转换运算符:
1)dynamic_cast
2)const_cast
3)static_cast
4)reinterpret_cast
一,dynamic_cast运算符
假设:
class High{};
class Low{};
Hign * ph;
Low * pl;
当且仅当 Low 是High的可访问基类(直接或间接)时,下面的语句才将一个Low*指针赋给pl
pl = dynamic_cast<Low*>(ph);
否则上句将空指针赋给pl.
综述:dynamic_cast运算符用途是,使得能够在类层次结构中进行向上转换(由于是is-a关系,这样的转换是安全的),不允许其它转换。
二,const_cast 运算符
这里解释下volatile:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
关于volatile的思考:
1)一个参数既可以是const还可以是volatile吗?解释为什么。
2)一个指针可以是volatile 吗?解释为什么。
3)下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
答案: 1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2)是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3)这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
const_cast 运算符该运算符用于执行只有 一种用途的类型转换,即改变值为 const 或 volatile.
表达式:const_cast<type_name>(experssion)
如果类型的其它方面也被修改,则上述类型的转换将出错,也就是说:
除了const 与volatile特征(有或无)可以不同外,type_name与expression的类型必须相同。
还是假设:High 与 Low 是两个类
High high;
const High * ph = &high;
High * ph2 = const_cast<High *>(ph); // valid -- High * 与 const Hign *,忽略const,相同
const Low * pl = const_cast<const Low *>(ph); // invalid -- const Low * 与 const Hign *,忽略const,不同
提供const_cast运算符的原因:
有时候可能需要这样一个值,它在大多数时是常量,但有时又要是可以修改的。
在这种情况下,可将这个值申明为const,并在修改它的时候,使用cosnt_cast。
当然也可以用通用的类型转换,但通用类型转换可以同时改变类型。
High high;
const High * ph = &high;
High * ph2 = (High *)ph; // valid
Low * pl = (Low *)ph; // also valid
由于编程时可能同时改变类型与常量特征,因此const_cast运算符更安全。const_cast 不是万能的,它可以修改指向一个值的指针,但可能无法修改const的值。
例:
#include <iostream>
using std::cout;
using std::endl;
void change(const int * pt, int n);
int main()
{
int pop1 = 38383; //非const值
const int pop2 = 2000; //const值
cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
change(&pop1, -103);
change(&pop2, -103);
cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
return 0;
}
void change(const int * pt, int n) //都用一个const类型的指针指向pop1,pop2
{
int * pc;
pc = const_cast<int *>(pt);
*pc += n;
}
结果:pop1, pop2: 38383, 2000
pop1, pop2: 38280, 2000
请按任意键继续. . .
上述结果说明了:
const_cast 可以删除 const int * pt 中的const,
但如果pt指向的值也是const,则编译器可能禁止修改它。
三,static_cast 运算符
表达式: static_cast<type_name>(expression)
仅当 type_name 可以被隐式转换为 expression 或 expression 可被隐式转换为 type_name所属的类型时,上述转换才合法,否则出错。
假设Hign是Low的基类,Pond是一个无关的类,则从High到Low的转换,从Low到High的转换都是合法的,但从Low到Pond的转换是不允许的。
High high;
Low low;
High * ph = static_cast<High *>(&low); // valid
Low * pl = static_cast<Low *>(&high); // valid
Pond * pp = static_cast<Pond *>(&low); // invalid
四,reinterpret_cast 运算符
表达式:reinterpret_cast<type_name>(expression)
该运算符用于天生危险的类型转换,它不允许删除const,但会做其它操作。
示例:
struct dat
{
short a;
short b;
};
long value = 0xA224B118;
dat * pd = reinterpret_cast<dat *>(&value);
std::cout << hex << pd->a; //display first 2 bytes of value
注:不同的操作系统在存储多字节整型时,可能以不同的顺序存储其中的字节。
reinterpret_cast 运算符并不支持所有的类型转换。
如:
1)可以将指针类型转换为足以存储指针表示的整型,但不能将指针转换为更小的整型或浮点型。
2)不能将函数指针转换为数据指针。
下面是对是错:
char ch = char(&d);
在C中是允许的,但在C++中通常不允许,因为char类型都太小,不能存储指针。