变量进行操作的时候要求类型,函数调用的时候要求实参类型与形参相同。此时就涉及到了类型转换。
类型转换分为隐式转换和显式转换。
1. 隐式转换
顾名思义,非人为控制的,编译器进行的类型转换,此种情况下程序员可能并不知道进行了转换。
如:
int i = 10;
i += 10.23; //要求加数也是int型,常数10.23默认为double型,隐式转换为int, 值截断变为10
编译时编译器也会提醒“ warning C4244: “+=”: 从“double”转换到“int”,可能丢失数据”, 因为8个字节的double型转换为4字节的int型时,会丢失高位的4个字节。
又如:
void PrintString(string str)
{
cout << str << endl;
}
void TestCast()
{
PrintString("mystring"); // "mystring"隐式转换为了string类型的临时局部变量
}
调用PrintString的时候,"mystring"作为string构造函数string(const char *)的实参初始化str。
2. 显式转换
将变量转换为指定的类型,即为显式转换。
C的风格,静态类型转换,比如:
(转换的类型)原类型的变量
void TestCast()
{
int i = 10;
int j = 0x10000;
short s = (short)i; // 编译器将不会提示数据截断,s的值与i相同
short u = (short)j; // 数据将发生截断: j的低位4个字节将赋值u,u的值将会是0,安全性需要程序员自己掌握
cout << "s: " << s << endl;
cout << "u: " << u << endl;
char *pNewBuf = (char *)malloc(1024); // malloc返回void *,将其转换为char *
delete pNewBuf;
}
C++中,C的模式仍然可以使用,对struct, class等也适用。
并提供了更加安全的转换操作符: static_cast、dynamic_cast、const_cast和reinterpret_cast。
const_cast,字面上理解就是去const属性。
static_cast,命名上理解是静态类型转换。如int转换成char。
dynamic_cast,命名上理解是动态类型转换。如子类和父类之间的多态类型转换。
reinterpreter_cast,仅仅重新解释类型,但没有进行二进制的转换。
转换的格式形如:static_cast<newtype>(var)
const_cast: 去除const\volatile属性
void PrintCString(char *sz)
{
cout << sz << endl;
}
void TestCast()
{
const char *cpstr = "this is const string for const_cast";
// PrintCString(cpstr); // 系统提示error C2664: “PrintCString”: 不能将参数 1 从“const char *”转换为“char *”
char *pstr = const_cast<char *>(cpstr); // 类似于 pstr = (char *)cpstr;
PrintCString(pstr);
}
reinterpreter_cast: 重新解释类型
仅仅复制了比特位,而没有进行分析。谨慎使用。(参看链接)
void TestCast()
{
const char *cpstr = "this is const string for const_cast";
int nPtrAddr = reinterpret_cast<int>(cpstr); // cpstr的值转换为int值
cout << hex << uppercase << showbase << nPtrAddr << endl; // 显示为大写的十六进制
}
static_cast: 类似于C风格的强制转换。无条件转换,静态类型转换
1. 基类和子类之间转换:其中子类指针转换成父类指针是安全的;但父类指针转换成子类指针是不安全的。(基类和子类之间的动态类型转换建议用dynamic_cast)
2. 基本数据类型转换。enum, struct, int, char, float等。static_cast不能进行无关类型(如非基类和子类)指针之间的转换。
3. 把空指针转换成目标类型的空指针。
4. 把任何类型的表达式转换成void类型。
5. static_cast不能去掉类型的const、volitale属性(用const_cast)。
class A
{
public:
virtual void Print() { cout<<"This is class A."<<endl; }
};
class B : public A
{
public:
void Print() { cout<<"This is class B."<<endl; }
};
class C
{
};
class D : public C
{
};
int ival = 0x10000;
short sval = static_cast<short>(ival); // 依旧会截断
D vd;
C *pC = static_cast<C *>(&vd); // 适用于没有虚函数的类
//pC = static_cast<C *>(&ival); // error: “static_cast”: 无法从“int *”转换为“C *”
B b;
A *pA = static_cast<A *>(&b);
dynamic_cast: 有条件转换,动态类型转换,运行时类型安全检查(转换失败返回NULL):
1. 安全的基类和子类之间转换。
2. 必须要有虚函数。
3. 相同基类不同子类之间的交叉转换。但结果是NULL。
class E : public A
{
public:
void Print() { cout<<"This is class E."<<endl; }
};
class F : public E, public B
{
public:
void Print() { cout<<"This is class F."<<endl; }
};
D d;
C *pC = dynamic_cast<C *>(&d); // 类C/D没有虚函数, 正常转换
B b;
A *pA = dynamic_cast<A *>(&b); // 正常转换
A a;
B *pB = dynamic_cast<B *>(&a); // pB将会是NULL
A &rA = dynamic_cast<A&>(a); // okay
rA.Print();
try{
B &rB = dynamic_cast<B&>(rA); // 将抛出bad_cast异常
rB.Print();
}
catch (std::bad_cast bc)
{
cout << bc.what() << endl;
}
//
/* F
/ \
E B
\ /
A
*/
F f;
A *pA2 = dynamic_cast<A *>(&f); // dynamic_cast 用于转换为不可访问或不明确的基;运行时测试将失败(“F *”到“A *”)
基本类型转换用static_cast。
多态类之间的类型转换用daynamic_cast。
不同类型的指针类型转换用reinterpreter_cast。(危险操作)