C++的类型转换

在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);
}

但是这样的转换有很大的缺陷:

  1. 转换的可视性非常差,所以的转换形式都是同一种方式书写的,很难去跟踪错误的转换
  2. 转换太过随意,可以在任意类型之间转换。你可以把一个指向const对象的指针转换成指向非const对象的指针,把一个指向基类对象的指针转换成一个派生类对象的指针,这些转换之间的差距是非常巨大的,但是传统的C语言风格的类型转换没有区分这些
  3. C风格的转换没有统一的关键字和标示符。对于大型系统,做代码排查时容易遗漏和忽略。

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

一、static_cast

​ 1.基本用法:static_cast expression

2.使用场景:

a、用于类层次结构中基类和派生类之间指针或引用的转换

上行转换(派生类---->基类)是安全的;

下行转换(基类---->派生类)由于没有动态类型检查,所以是不安全的。

b、用于基本数据类型之间的转换,如把int转换为char,这种带来安全性问题由程序员来保证

c、把空指针转换成目标类型的空指针

d、把任何类型的表达式转为void类型

3.使用特点

a、主要执行非多态的转换操作,用于代替C中通常的转换操作

b、隐式转换都建议使用static_cast进行标明和替换

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

二、reinterpret_cast

​ 1.基本用法:reinterpret_cast expression

2.使用场景:用于将一种类型转换为另一种不同的类型(不到万不得已,不用使用这个转换符,高危操作)

3.使用特点:

a、reinterpret_cast是从底层对数据进行重新解释,依赖具体的平台,可移植性差

b、reinterpret_cast可以将整型转换为指针,也可以把指针转换为数组

c、reinterpret_cast可以在指针和引用里进行肆无忌惮的转换

typedef void (* FUNC)();

int DoSomething (int i)
 {
	cout<<"DoSomething" <<endl;
	return 0;
 }

void Test ()
 {
//
// reinterpret_cast可以编译器以FUNC的定义方式去看待DoSomething函数
// 所以非常的BUG,下面转换函数指针的代码是不可移植的,所以不建议这样用
// C++不保证所有的函数指针都被一样的使用,所以这样用有时会产生不确定的结果
//
	FUNC f = reinterpret_cast< FUNC>(DoSomething );
	f();
 }

三、const_cast

1.基本用法:const_cast expression

2.使用场景:

a、常量指针转换为非常量指针,并且仍然指向原来的对象

b、常量引用被转换为非常量引用,并且仍然指向原来的对象

3.使用特点:

a、cosnt_cast是四种类型转换符中唯一可以对常量进行操作的转换符

b、去除常量性是一个危险的动作,尽量避免使用。一个特定的场景是:类通过const提供重载时,一般都是非常量函数调用const_cast将参数转换为常量,然后调用常量函数,然后得到结果再调用const_cast 去除常量性。

void Test ()
 {
	const int a = 2;
	int* p = const_cast< int*>(&a );
	*p = 3;
	cout<<a <<endl;
 }

四、dynamic_cast

​ 1.基本用法:dynamic_cast expression

2.使用场景:只有在派生类之间转换时才使用dynamic_cast,type-id必须是类指针,类引用或者void*。

3.使用特点:

a、基类必须要有虚函数因为dynamic_cast是运行时类型检查,需要运行时类型信息,而这个信息是存储在类的虚函数表中,只有一个类定义了虚函数,才会有虚函数表(如果一个类没有虚函数,那么一般意义上,这个类的设计者也不想它成为一个基类)。

b、对于下行转换,dynamic_cast是安全的(当类型不一致时,转换过来的是空指针),而static_cast是不安全的(当类型不一致时,转换过来的是错误意义的指针,可能造成踩内存,非法访问等各种问题)

c、dynamic_cast还可以进行交叉转换

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

向下转型:父类对象指针->子类指针/引用(用dynamic_cast转型是安全的)

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;
}

五、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;   //报错(初始化”: 无法从“int”转换为“A”)
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值