C++11(五):类型转换全解析

目录

c++的类型转换

1. C语言中的类型转换

2. 为什么c++需要四种类型转换

3. c++强制类型转换解析

3.1 static_cast

3.2 reinterpret_cast

3.3 const_cast

3.4 dynamic_cast

4. RTTI


c++的类型转换

c语言和c++是一个强类型的语言,也就是变量和变量之间有明显的类型的区分

1. C语言中的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换

1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败

2. 显式类型转化:需要用户自己处理

缺陷:转换的可视性比较差,所有的转换形式都是以一种相同的形式书写,难以跟踪错误转换

2. 为什么c++需要四种类型转换

标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:

static_castreinterpret_castconst_castdynamic_cast

C风格的转换格式很简单,但是有不少缺点的:

1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失

2. 显式类型转换将所有情况混合在一起,代码不够清晰

因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格

c++兼容c语言留下来的隐式类型转换和显示转换,但是c++觉得c语言做的不规范,c++想规范一下,标准c++为了加强类型的可视性,引入了四种命名的强制类型转换操作符

3. c++强制类型转换解析

3.1 static_cast

对于c语言中的隐式类型转换,相近的类型就使用这个

int i = 1;
double d = 8.88;
i = static_cast<int>(d);
cout << i << endl;

3.2 reinterpret_cast

对于c语言中的强制类型转换,不相近的类型就用这个

int i = 1;
double d = 8.88;
i = static_cast<int>(d);
cout << i << endl;
int* p = nullptr;
p = reinterpret_cast<int*>(i);
cout << p << endl;

总结:如果你不相近的类型使用了static_cast就不可以,如果相近的类型使用了reinterpret_cast也不可以,总之c++是为了实现你一看用哪个cast就知道,但注意你并没有改变原来变量的类型,也就是d还是8.88,还是double类型

3.3 const_cast

const_cast就是去除const属性的

const int ci = 10;
int* pi = const_cast<int*>(&ci);
*pi = 20;
cout << *pi << endl;
cout << ci << endl;

这种情况是未定义行为:

原变量是const修饰的,但是经你这么修改,原变量本来是const,但是可以通过指针来修改,这种是不合法的,结果是未定义的

// 可能的结果:
// 1. 程序崩溃;
// 2. 输出 ci 仍为 10(编译器优化,a 被存储在只读内存/寄存器);
// 3. 输出 ci为 20(极少数未优化场景)。

我的编译器优化之后ci仍然为10,原因

可以看到通过调试已经修改了变量的值,但是为什么打印ci是10

这是是去寄存器当中找的,就是因为你原来是const对象,它认为你是不可能修改的,所以放到寄存器当中,这样速度快,但是你这里修改的是内存当中的值(ci当中内存是变20了),然后打印的时候由于ci寄存器有直接就拿了,本质是由于编译器对const对象存取优化机制导致的,甚至有些直接优化成常量10了

为了避免编译器优化,可以通过加volatile关键字,让编译器每次都到内存当中取

volatile const int ci=10;

但是一般不要这么编写,本来好好的const常量被你修改了之后能变

一般都是const指针或者const引用去除const属性(只有这种场景是合法的)

3.4 dynamic_cast

用于多态类型的转换

c++中子类对象可以赋值给父类的对象、指针、引用、这个过程是语法天然支持的,可以成功

https://blog.youkuaiyun.com/Laydya/article/details/148145002

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)

向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则) 向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)

注意: 1. dynamic_cast只能用于含有虚函数的类 2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0

class A {
public:
	virtual void f() {}
	int _a;
};
class B :public A {
public:
	int _b;
};
void f_cast(A* pa) {
	//如果想要区分pa是指向父类,还是子类对象?
	B* pb = (B*)pa;
	pb->_a = 1;
	pb->_b = 2;
}
int main() {
	A a;
	B b;
	A* pa = &a;
	f_cast(pa);
	B* pb = &b;
	f_cast(pb);
	return 0;
}

这里强制类型转换了之后,其实就是这个pb指针能看到的东西更多了,也就是指针的意义就是根据类型能够看到多大的空间,由于你原来是A的类型,但是你这里强制类型转换成b了,所以看到的东西变多了,然后你pb-> _b  就会越界访问

如果你本身就是一个子类对象,那就没问题,因为你本身就可以看到这么多东西


class A {
public:
	virtual void f() {}
	int _a;
};
class B :public A {
public:
	int _b;
};
void f_cast(A* pa) {
	//如果想要区分pa是指向父类,还是子类对象?
	B* pb = dynamic_cast<B*>(pa);
	if (pb != nullptr) {
		cout << "转换成功" << endl;
		pb->_a = 1;
		pb->_b = 2;
	}
	else {
		cout << "转换失败" << endl;
	}
}
int main() {
	A a;
	B b;
	A* pa = &a;
	f_cast(pa);
	B* pb = &b;
	f_cast(pb);
	return 0;
}

使用dynamic_cast,如果pa是指向子类对象,则转换成功

反之如果是父类对象,则转换失败

注意:只能针对继承中的多态类型(父类必须包含虚函数)如果你把A里面的虚函数去掉那就编译不过去了

原理:dynamic_cast通过去虚表的上方存储的标识信息,来判断指向父类对象还是子类对象,如果没有虚函数,则没有虚表,就无法判断了

总结:尽量少用类型转换,如果要用,就用规范一些,让别人一看到就知道你在干嘛

4. RTTI

RTTI(Run-Time Type Information,运行时类型信息)是 C++ 提供的在程序运行阶段获取对象 / 指针实际类型信息的机制,核心作用是打破编译期的类型绑定,让程序在运行时识别多态类型(比如基类指针指向的派生类对象的真实类型)。

C++ 标准中,RTTI 的支持是可选的(但主流编译器如 GCC/Clang/MSVC 均默认开启),且仅对带有虚函数的类(多态类)能正确返回动态类型;非多态类的 RTTI 仅能返回编译期类型。

C++通过以下方式来支持RTTI:

1. typeid运算符

typeid 运算符返回 std::type_info 对象,包含类型的名称、哈希值等信息,可用于:

  • 比较两个对象 / 指针的实际类型是否相同;

  • 获取类型的名称(注意:名称格式由编译器决定,不一定是类的原名)。

  • 只有多态才能返回指向的类型,如果没有多态返回的是编译期类型(基类),而非实际指向的派生类

2. dynamic_cast运算符

这个上面已经做了详细的讲解,也就是通过运行时的类型确定的,运行起来后通过查虚表,虚表里面有标识来辨别实际类型,仅仅针对多态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值