C++之 强制类型转换(static_cast、reinterpret_cast、const_cast、dynamic_cast)


引入

类型转换这个概念在许多编程语言中都有涉及,比如最经典的编程语言: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);
}

输出结果

1, 1.00
28fb78, 268516

代码虽然会有警告格式字符串“%x”需要类型“unsigned int”的参数,但可变参数 1 拥有了类型“int *”,但是编译没有问题,还是可以正常运行。

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

  1. static_cast
  2. reinterpret_cast
  3. const_cast
  4. dynamic_cast

static_cast

static_cast用于非多态类型的转换(静态转换

编译器隐式执行的任何类型转换都可static_cast,但它只能用于两个相关类型的转换,不相关类型不能转换。
(何为不相关?例如intint*,编译器对于二者的理解没有强相关性,一个为整型数据,一个为地址,所以不可以使用static_cast进行强制转换)。

用法

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

输出结果

88

很明显static_cast将同为实数类型的整型与浮点数进行了强制转换。


reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型

reinterpret_cast将数据的二进制形式重新解释,但是不改变其值:

int main() {
	int i;
	const char* ptr = "1";
	i = reinterpret_cast<char>(ptr);
	std::cout << i << std::endl;
	system("pause");
	return 0;
}

输出结果

64

警告:“reinterpret_cast”: 从“const char *”到“char”截断


const_cast

const_cast的用途就是删除变量的const属性,方便赋值,例如把const类型的指针变为非const类型的指针。

int main() {
	const int a = 21;
	int* modifier = const_cast<int*>(&a);
	*modifier = 7;
	cout << *modifier << endl;
	return 0;
}

输出结果

7

这里要提一个没有使用过的关键字:volatile保持内存可见性,防止编译器过度优化

volatile const int a = 21;
  • 使用了volatile关键字之后发生了什么?
  • 因为const关键字使得编译器默认a变量是const类型,所以编译读取一次a的值之后就默认这个值不会改变了,对a读取操作也会直接从寄存器上取出使用这个值,等效替代读取a。如果是修改就没有影响。
    这个例子可能没有很好的体现问题,但是我们确实进行了修改,所以为了更加科学,就要让编译器时刻保持与内存的数据同步(可见性),不要出现类似于数据库相关的“幻读”或者“脏读”现象(扯远了。。),所以底层执行输出时进行了一次同步而不是使用寄存器值,读取了a中的7,进行输出。

dynamic_cast

  • 该操作符用于运行时检查该转换是否类型安全,但只在多态类型时合法,即被转换的类至少具有一个虚拟方法。

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

  • 向上转型:如果子类对象指针转换为父类指针或引用,此时不需要转换,因为赋值兼容规则)。
  • 向下转型:如果父类对象指针转换为子类指针或引用,用dynamic_cast转型是安全的。

注意:

  1. dynamic_cast只能用于含有虚函数的类。
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
class A{
public:
	virtual void f() {}
};
class B : public A
{};
void fun(A* pa){
	// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
	B* pb1 = static_cast<B*>(pa);
	B* pb2 = dynamic_cast<B*>(pa);

	cout << "pb1:" << pb1 << endl;
	cout << "pb2:" << pb2 << endl;
}
int main(){
	A a;
	B b;
	fun(&a);
	fun(&b);
	return 0;
}

输出结果

pb1:0019F8F0
pb2:00000000		//未转换成功
pb1:0019F8E4
pb2:0019F8E4

其实 dynamic_cast是一个泛型

模拟实现

template<class PDirve, class Base>
PDirve my_dynamic_cast(Base* p){
	if (p->is_base()){
		return nullptr;
	}
	else{
		return (PDirve)p;
	}
}
  • 小结:
    强制类型转换关闭挂起了正常的类型检查,每次使用强制类型转换前,程序管理者应该仔细考虑是否还有其他不同的方法达到同一目的,如果非要使用强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。

既然容易出问题,那么还是建议防患于未然:避免使用强制类型转换


explicit关键字

explicit关键字阻止经过转换构造函数进行的隐式转换的发生。

class A{
public:
	explicit A(int a){
		cout << "A(int a)" << endl;
	}
	A(const A& a){
		cout << "A(const A& a)" << endl;
	}
private:
	int _a;
};
int main(){
	A a1(1);	// 隐式转换-> A tmp(1); A a2(tmp);
	A a2 = 1;
}

程序会报错:没有intA的适当的构造函数,因为我们通过explicit禁止了它的单参数构造。


强转的必要性

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

  1. 隐式类型转化有些情况下可能会出问题。
  2. 显式类型转换将所有情况混合在一起,代码不够清晰。

所以C++的强制类型转换还是有用武之地的。


RTTI:运行时类型识别

RTTI:(Run-time Type identification)即:运行时类型识别。

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

  1. typeid运算符。
  2. dynamic_cast运算符。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

giturtle

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值