一、回顾c语言类型转换
隐式类型转换:整型之间,浮点数与整型之间。即有关联的类型之间可以隐式类型转换。
强制类型转换:整型与指针之间。
int main()
{
int i = 1;
// 隐式类型转换
double d = i;
printf("%d, %.2f\n", i, d);
int* p = &i;
// 显示的强制类型转换
int address = (int)p;
printf("%x, %d\n", p, address);
return 0;
}
运行结果:
二、c++类型转换
内置类型 -> 自定义类型:构造函数实现
自定义类型 -> 自定义类型:构造函数实现
自定义类型 -> 内置类型:重载operator
class A
{
public:
A(int a1)
:_a1(a1)
{}
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{}
operator int()
{
return _a1 + _a2;
}
int get() const
{
return _a1 + _a2;
}
private:
int _a1 = 1;
int _a2 = 1;
};
class B
{
public:
B(const A& aa)
:_b1(aa.get())
{}
private:
int _b1;
};
int main()
{
//内置类型->自定义类型 构造函数实现
A a1 = 1;
A a2 = { 2, 2 };
//自定义类型->内置类型 operator int()实现
int x = a1;
cout << x << endl;
//自定义类型->自定义类型 构造函数实现
B b1(a1);
return 0;
}
运行结果:
三、c++四种类型转换
为加强类型转换的可视性,引入四种命名的强制类型转换操作符。
1、static_cast 静态转换
类似隐式类型转换,适用于关联度高的类型转换。
2、reinterpret_cast
类似强制类型转换,适用于关联度低的类型转换。
int main()
{
int i = 1;
// 隐式类型转换
double d = static_cast<int>(i);
printf("%d, %.2f\n", i, d);
int* p = &i;
// 显示的强制类型转换
int address = reinterpret_cast<int>(p);
printf("%p, %d\n", p, address);
return 0;
}
3、const_cast
用于删除变量的 const 属性,方便赋值。
int main()
{
volatile const int a = 2;
int* p1 = const_cast<int*>(&a);
*p1 = 3;
cout << a << endl;
return 0;
}
首先 const int a; a变量不是存在静态区,而是在栈上,叫做常变量。
int main()
{
const int a = 2;
int* p1 = const_cast<int*>(&a);
*p1 = 3;
cout << a << endl;
return 0;
}
理论上输出的结果应该是3,在内存管理器中查看到的值也是3,但是打印结果还是2,原因是由于编译器的优化,遇到 const 默认只从内存中取一次变量的值(a = 2)放到寄存器,此时改变即使内存中值是3,但编译器只从寄存器中读2,加 volatile 关键字就是告诉编译器每次取变量从内存中取。
4、dynamic_cast
向下转换:用于将一个父类对象的指针 / 引用转换成子类对象的指针 / 引用。
向上转型:子类对象指针 / 引用 -> 父类对象指针 / 引用(切片,赋值兼容)
向下转型:父类对象指针 / 引用 -> 子类对象指针 / 引用(dynamic_cast 转型最安全,强转不安全)
dynamic_cast用于原先是子类对象,由于切片变成父类对象,再次转换成子类对象,即用于判断是否经过切片。如果转换成功,返回子类对象,转换失败,返回nullptr
注意:
1、dynamic_cast 只能用于父类有虚函数的类,可以理解为转换需要用到虚函数
2、先检查是否能转换成功,成功就转换,不成功返回0
3、RTTI:Run-time Type identification的简称,即运行时类型识别。 C++通过以下方式来支持RTTI: 1. typeid运算符 2. dynamic_cast运算符 3. decltype