从 C 向 C++ 进阶系列导航
1. 隐式类型转换
在 C 中支持隐式类型转换。所谓的隐式类型转换为编译器自动地完成不同的数据类型之间赋值转换。低类型到高类型的转换是安全的,反之是不安全的,可能会发生数据截断。这里的低类型指的是占用存储空间较小的数据类型。
隐式类型转换发生在如下情形:
- 不同等级类型之间运算中,低类型转换为高类型。
- 赋值表达式中,右边表达式(先运算)的值转换为左边变量的类型。
- 函数调用时,实参转换为形参的类型。
- 函数返回时,return 表达式转换为返回类型。
以下为缺省的低类型向高类型的隐式转换方向:
由于 C++ 为强类型语言,因此不允许隐式类型转换。
2. 强制类型转换
C 与 C++ 均支持强制类型转换。所谓的强制类型转换为直接在变量前使用括号包含需要转换的目标数据类型,表示临时地强制按该数据类型解析该变量代表的存储内容。
- 示例:
float pi = 3.14; // pi = 3.14
int num = (int)pi; // num = 3
long* p = (long*)#
注意:基本类型(整型、字符型等)不能与自定义类型(结构体)进行相互转换。
特别地,函数指针的类型转换需把函数指针声明中的变量名和声明末尾的分号去掉,再把剩余部分用括号括起来即可。例如:float (*p)();
表示定义了一个指向返回值为浮点类型的函数的指针 p,因此 (float (*)())
表示为“指向返回值为浮点类型的函数的指针”的类型转换符。
3. C++ 新的强制类型转换
由于 C 方式的强制类型转换过于简单粗暴,允许任意类型之间相互转换,存在安全隐患,因此 C++ 提供了新的强制类型转换方式进行替代,通过引入 static_cast、dynamic_cast、const_cast、reinterpret_cast 这四个关键字处理不同类型间转换的场景。
3.1 静态类型转换
静态类型转换是在编译期内即可决定其类型的转换,遵循以下规则:
- 可用于基本类型间的转换。
- 不能用于基本类型指针间的转换。
- 可用于有继承关系类对象间的转换(父类不能向子类转换)
- 可用于有继承关系类指针间的转换(无限制)
语法格式:static_cast<目标类型> (标识符)
。
- 实验:
class Parent
{
};
class Son : public Parent
{
};
class Other
{
};
int main()
{
/* 基础类型转换 */
float pi = 3.14;
int num = static_cast<float>(pi); // 基础类型的转换
cout << "num = " << num << endl; // num = 3
/* 基础类型指针转换 */
float *pfloat = π
// int *pint = static_cast<int *>(pfloat); // error: invalid static_cast from type ‘float*’ to type ‘int*’
void *pvoid = static_cast<void *>(&pi); // no error
/* 继承关系类转换 */
Parent par_obj;
Son son_obj;
// son_obj = static_cast<Son>(par_obj); // error: no matching function for call to ‘son::Son(Parent&)’
par_obj = static_cast<Parent>(son_obj); // no error
/* 继承关系类指针转换 */
Parent *p_par = &par_obj;
Son *p_son = &son_obj;
p_par = static_cast<Parent *>(&son_obj);
p_son = static_cast<Son *>(&par_obj);
/* 非继承关系类指针转换 */
// Other *p_oth = static_cast<Other *>(&son_obj) // error: invalid static_cast from type ‘Son*’ to type ‘Other*’
return 0;
}
3.2 动态类型转换
动态类型转换为在程序运行期进行的类型转换,遵循以下规则:
- 可用于有继承关系类指针间的转换。
- 可用于有交叉关系类指针间的转换。
- 具有类型检查功能。
- 必须有虚函数支持。
当用于指针时,转换成功将得到目标类型的指针,转换失败的话将得到的是一个空指针。当用于引用时,转换成功将得到目标类型的引用,转换失败的话将产生一个异常操作信息
语法格式:dynamic_cast<目标类型> (标识符)
。
- 实验:
class Base
{
public:
virtual ~Base(){}
};
class Derived : public Base
{
};
int main()
{
Base* p = new Derived;
Derived* pd = dynamic_cast<Derived*>(p);
if( pd != NULL )
{
cout << "pd = " << pd << endl; // pd = 0x23adc20
}
else
{
cout << "Cast error!" << endl;
}
delete p;
return 0;
}
3.3 常类型转换
常类型转换为在程序编译期间进行的类型转换,遵循以下规则:
- 可用于去除变量的只读属性。
- 转换的目标类型必须为指针或引用。
语法格式:const_cast<目标类型> (标识符)
。
- 实验:
int main()
{
int num = 0;
const int var = num; // 防止进入符号表,原理见 const 关键字章节
/* 去除变量的只读属性 */
const int* p = &var;
// *p = 1; // assignment of read-only location ‘* p’
int* tmp_p = const_cast<int*>(&var);
*tmp_p = 1;
cout << "var = " << var << endl; // var = 1
/* 去除变量的只读属性 */
const int& ref = var;
// ref = 2; // error: assignment of read-only reference ‘ref’
int& tmp_ref = const_cast<int&>(var);
tmp_ref = 2;
cout << "var = " << var << endl; // var = 2
/* 对象类型必须为指针或者引用 */
// int tmp = const_cast<int>(var); // error: invalid use of const_cast with type ‘int’, which is not a pointer, reference, nor a pointer-to-data-member type
}
3.4 解析类型转换
解析类型转换为在程序编译期间进行的类型转换,遵循以下规则:
- 可用于指针间的转换。
- 可用于整型向指针的转换。
语法格式:reinterpret_cast<目标类型> (标识符)
。
- 实验:
int main()
{
/* 指针间的转换 */
int num = 0;
int* ip = #
char* cp = reinterpret_cast<char*>(ip);
float* fp = reinterpret_cast<float*>(cp);
/* 整形向指针的转换 */
char* p_A = reinterpret_cast<char*>(NULL); // 指向 0 地址
char* p_B = reinterpret_cast<char*>(num); // 指向地址为 num 的值的内存
}