C++:C++中的类型转换(static_cast、reinterpret_cast、const_cast、dynamic_cast)

C语言中的类型转换

隐式类型的转换(相近类型)
显示强制类型的转换(不相关类型)

void Test()
{
	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);
}

其中i是int型,d是double型,不同类型的类型之间按理说不可以进行赋值,但是这里可以,因为发生了隐式类型的转换。
C语言中的类型转换,转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换,尤其是隐式类型的转换,编译器悄悄的给你换了你还不知道(╬ ̄皿 ̄)。

C++中的类型转换

static_cast

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。也就是说static_cast可以执行指针到指针的转换,或实例本身到实例本身的转换,但不能在实例和指针之间转换,它必须用于相近类型之间。static_cast只能提供编译时的类型安全。
其实就可以简单的理解为是C语言中的隐式类型的转换。

int main()
{
	double d = 12.34;
	int a = static_cast<int>(d);
	cout << a << endl;
	return 0;
}

这里输出的a是12,发生了隐式类型的转换,把后面的精度丢失了。

reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型。
也就是说reinterpret_cast可以转换任意一个32bit整数,包括所有的指针和整数。可以把任何整数转成指针,也可以把任何指针转成整数,以及把指针转化为任意类型的指针,威力最为强大!但不能将非32bit的实例转成指针。总之,只要是32bit的东东,怎么转都行!

可以理解为它是C语言中的强制类型转换。

int main()
{
	int i = 10;
	int *j = &i;
	int address =reinterpret_cast<int> (j);
	cout << j << endl;
	cout << address << endl;
	system("pause");
	return 0;
}

在这里插入图片描述
reinterpret_cast还可这样做一件很bug的事,如下,可以不用参数调用一个函数

typedef void(*FUNC)();//无参无返回值(函数指针)
int DoSomething(int i)//有参有返回值
{
	cout << "DoSomething" << endl;
	return 0;
}
int main()
{
	FUNC f = reinterpret_cast<FUNC> (DoSomething);//强制类型的转换
	f();
	system("pause");
	return 0;
}

在这里插入图片描述
我们发现这样把DoSomething函数强转之后,可以不用参数调到DoSomething函数。

const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值。

int main()
{
	const int a = 2;
	int* p = const_cast< int*>(&a);
	*p = 3;
	cout << a << endl;
	system("pause");
	return 0;
}

如这一段代码,&a的类型是const int*,要赋值给p就要去掉const属性,就可以使用const_cast进行类型转换。
这段代码输出的值为2,虽然说让*p等于3了,但是由于const修饰的变量,编译器信任它,觉得它不会进行修改了,编译器在运行的时候会把这个变量的值放在寄存器里,以后每次访问都会去寄存器里拿,因为从寄存器里拿会很快。因此虽然让 *p等于3,但是改变的只是内存里a的值,输出的时候编译器还是会在寄存器里拿,因此输出的还是2。
如果想要程序结果输出3的话,就要加上volatile关键字,这个关键字就保证了内存的可见性,保证读取数据是在内存里拿的。

dynamic_cast

子类的指针给父类的指针时不需要强转(因为会发生切片),但是父类的指针给子类的指针时就需要强转,但是强转的时候不一定都是正确的,因为一个父类的指针有可能指向一个父类的对象也有可能指向一个子类的对象,当这个父类的指针指向一个子类的对象时再把它强转为子类是正确的,但是如果这个父类的指针指向一个父类的对象时再把它强转为子类就发生了错误(越界)。

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

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

注意:

  1. dynamic_cast只能用于含有虚函数的类 (必须是构成多态的类)
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0

父字类型我们可以认为是相近的类型,因此我们可以使用static_cast进行强转,但是如果父类的指针指向一个父类的对象时再把它转换为子类时,static_cast也会进行转换。而dynamic_cast遇到这种情况会进行判断,如果是父类对象的指针强转为子类对象的指针时dynamic_cast就会返回0。

class A
{
	virtual void f(){}
};
class B :public A
{};

void test(A* p)
{
	//不会进行检查
	//A* pp = (B*)p;
	//A* pp = static_cast<B*> (p);
	//cout << pp << endl;

	//会先进行检查,如果可以转就转,不可以转就返回0
	A* pp = dynamic_cast<B*> (p);
	cout << pp << endl;
}
int main()
{
	A a;
	B b;
	test(&a);//传过去的是一个父类的指针,强转成子类会出问题
	test(&b);//传过去的是一个子类的指针,强转过程不会出问题
	system("pause");
	return 0;
}

在这里插入图片描述

强烈建议:避免使用强制类型转换

因为强制类型转换关闭或挂起了正常的类型检查,所以每次使用强制类型转换前,都应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。

强制类型转换其实是把类型的检查放在了运行阶段,所以能在编译时解决掉的问题没必要留到运行时、能用多态搞定的事情也没必要使用 dynamic_cast。

所以其实真正需要用到 dynamic_cast 的地方就不多了。除了需要在虚基类或者多继承等较复杂的类层次结构中漫游,对于简单的单继承关系,一般子类和父类的指针偏移为0(即:父类对象和子类对象的this指针相同),因此不需要通过 RTTI 信息来计算指针偏移。

  1. dynamic_cast 是有可能抛出 std::bad_cast 异常的
  2. C++ 的 RTTI 整体上比较弱(比如无法枚举成员函数)
  3. dynamic_cast 性能依赖于编译器,如果是通过字符串比较实现的,性能堪忧
  4. 跟大多数语言不一样,由于多继承的存在,C++ 的类型转换可能会改变指针的值
  5. 使用 dynamic_cast 的代码是难于测试的,你无法通过接口确认它到底依赖于哪些具体类,测试代码会比较复杂。并且增加了子类就要修改测试代码。

RTTI

RTTI:Run-time Type identification的简称,即:运行时类型识别
C++通过以下方式来支持RTTI:

  1. typeid运算符
  2. dynamic_cast运算符
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值