C++:类型转换
目录
一、C语言中的类型转换
二、C++四种强制类型转换
static_cast
reinterpret_cast
const_cast
dynamic_cast
C++查找手册https://legacy.cplusplus.com/
一、C语言中的类型转换
模糊的讲,所有的类型转换都有一个不成文的约束:只有彼此有一定关联的类型才可以互相转换。
C语言中的类型转换分为:
- 隐式类型转换
- 强制类型转换(显式类型转换)
发生隐式类型转换的情况:
1.整型家族之间(int、short、char)
int i = 10;
char c = i;//编译通过,隐式类型转换
2.整型和浮点数之间
int i = 10;
double d = i;//编译通过、隐式类型转换
3.bool类型和整型
if(0)
while(1)
4.bool类型和指针
while(ptr)
需要强制类型转换的情况
1.指针和整型
int i = 10;
int* p = &i;
int a = (int)p;//如果不强制类型转换,编译会报错
2.不同类型的指针之间
int i = 10;
int* p = &i;
char c = 'a';
char* pc = &c;
p = (int*)pc;//如果不强制类型转换,编译会报错
而C++使用面向对象的思想,C++中的类型分为内置类型和自定义类型,内置类型的互相转换规则同C语言一样,因此,C++的类型转换更多的讨论是关于内置类型和自定义类型之间。
自定义类型和内置类型之间的转换是通过构造函数支持的。
1.内置类型转换为自定义类型,由相应的构造函数实现,属于隐式类型转换
string s = "abcd";//本质是string支持const char* 的构造函数
const string& ss = "abcd";//引用要使用const,因为类型转换中间都会产生具有常性的临时变量
2.自定义类型转换为自定义类型,也是由构造函数支持
3.自定义类型转换为内置类型,需要特殊的重载函数,比如自定义类型A要转换为int
class A
{
public:
operator int()
{
return _a1 + _a2;
}
private:
int _a1 = 1;
int _a2 = 2;
};
以上的类型转换规则,由于继承C的风格,有一些缺陷:转换的可视性差,难以跟踪错误的转换。
因此C++在保证兼容C风格的类型转换前提下,引入了四种命名形式的类型转换操作符。
二、C++四种强制类型转换
static_cast
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可以用static_cast,但它不能用于两个不相关的类型进行转换。
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
return 0;
}
reinterpret_cast
reinterpret_cast用来规范原来的强制类型转换。
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
// 这里使用static_cast会报错,应该使用reinterpret_cast
//将int转换为指针
int* p = reinterpret_cast<int*>(a);
return 0;
}
const_cast
const_cast一般用来去掉变量的const属性。
void Test()
{
const int a = 2;
int* p = const_cast<int*>(&a);//注意<>的类型一般为指针或引用
*p = 3;
cout << a << endl;
}
运行结果
可见我们的要求并没有实现(不是)。其实在内存中变量a的值已经修改为了3,但是由于寄存器中还存储着原来的值(因为是常量,并不会轻易修改),所以去掉const属性是会造成内存可视优化风险的,因此新增关键字来解决。
void Test()
{
volatile const int a = 2;
int* p = const_cast<int*>(&a);
*p = 3;
cout << a << endl;
}
dynamic_cast
dynamic_cast,在继承体系中,一般用于将父类对象的指针或引用,动态的转换为子类对象的指针或引用(向下转换)。
在继承体系中,有两种转换的说法。
- 向上类型转换
向上转换是指子类对象“转换”为父类对象,本质不是类型转换,而是赋值兼容规则。
class A
{
public:
int _a = 0;
virtual void f() {}//该继承体系须支持多态
};
class B : public A
{
public:
int _b = 1;
};
int main()
{
//向上类型转换,其实不是类型转换,而是赋值兼容规则,子类向上“转换”为父类
B bb;
A aa = bb;
A* ptr = &bb;
return 0;
}
- 向下转换
向下转换是指父类对象“转换”为子类对象。
void fun(A* pa);//A是父类
根据继承中的语法规则
如果pa原来指向子类,此时向下转换是安全的。
如果pa原来指向父类,此时向下转换为子类,会造成越界访问,是不安全的。
而dynamic_cast就是用来动态转换父子类。
void fun(A* pa)
{
// 向下转换:父->子
// pa指向子类对象,转回子类,是安全的
// pa指向父类对象,转回子类,是不安全的,存在越界的风险问题
// 传统写法,不安全
//B* pb = (B*)pa;
// pa指向子类对象,转回子类,正常转换
// pa指向父类对象,转回子类,转换失败
B* pb = dynamic_cast<B*>(pa);
if (pb)
{
cout << pb << endl;
cout << pb->_a << endl;
cout << pb->_b << endl;
}
else
{
cout << "转换失败" << endl;
}
}
RTTI(Run-time Type identification)运行时类型识别
C++通过以下方式实现RTTI:
- typeid运算符
- dynamic_cast运算符
- decltype