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

C++ 类型转换

  • C语言的类型转换
  • C++的类型转换
    • static_cast
    • const_cast
    • reinterpret_cast
    • dynamic_cast
      • RTTI
      • dynamic_cast 和 static_cast
    • 单参构造函数的类型转换功能
      • explicit

C语言的类型转换

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

  1. 隐式类型转换:如整形提升、算术转换等,详见C语言隐式类型转换
  2. 显示类型转换:即显示的进行强制类型转换。 TYPE b = (TYPE) a;
    强制类型转换其实就是改变对内存数据的解析方式,虽然有时方便快捷,但并没有类型检查、不安全,所以可能在调用时出错(范围大的类型强转类型赋值给范围小的, 但是可能精度丢失)
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);
}

C++的类型转换

C风格的强制类型转换统一使用(),而()在代码中随处可见,所以也不利于使用检索工具定位强转的代码位置。
C++ 对类型转换进行了分类,并新增了四个关键字来予以支持,它们分别是:

关键字 说明
static_cast 用于良性转换,一般不会导致意外发生,风险很低。
const_cast 用于 const 与非 const、volatile 与非 volatile 之间的转换。
reinterpret_cast 高度危险的转换,这种转换仅是对二进制位的重新解释,不会借助已有的转换规则对数据进行调整,但是可以实现最灵活的 C++ 类型转换。
dynamic_cast 借助 RTTI,用于类型安全的向下转型(Downcasting)。

语法格式为: xxx_cast< newType > (data)

static_cast

静态转换,编译器隐式执行的任何类型转换都可用static_cast
编译期间转换,转换失败的话会抛出一个编译错误。

long n = static_cast<long>(m);  //宽转换,没有信息丢失
char ch = static_cast<char>(m);  //窄转换,可能会丢失信息
int *p1 = static_cast<int*>( malloc(10*sizeof(int)) ); //将void指针转换为具体类型指针
void *p2 = static_cast<void*>(p1);  //将具体类型指针,转换为void指针
double real= static_cast<double>(c);  //调用类型转换函数
    
//下面的用法是错误的
float *p3 = static_cast<float*>(p1);  //不能在两个具体类型的指针之间进行转换
p3 = static_cast<float*>(0X2DF9);  //不能将整数转换为指针类型

const_cast

进行去除 const 属性的转换,也是四个强制类型转换运算符中唯一能够去除 const 属性的运算符。

const string s = "Inception";
// 将 const 引用转换为同类型的非 const 引用
string& p = const_cast <string&> (s);
// 将 const 指针转换为同类型的非 const 指针
string* ps = const_cast <string*> (&s);  // &s 的类型是 const string*

贴上两个讲解 const 的好文,可以了解学习:
const修饰的变量的存储位置
【C++】const关键字详解 && volatile、mutable

reinterpret_cast

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

用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作

class A
{
public:
    int i;
    int j;
    A(int n):i(n),j(n) { }
};
int main()
{
    A a(100);
    int &r = reinterpret_cast<int&>(a); //强行让 r 引用 a
    r = 200;  //把 a.i 变成了 200
    cout << a.i << "," << a.j << endl;  // 输出 200,100
    
    int n = 300;
    A *pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 n
    pa->i = 400;  // n 变成 400
    pa->j = 500;  //此条语句不安全,很可能导致程序崩溃
    cout << n << endl;  // 输出 400
    
    long long la = 0x12345678abcdLL;
    pa = reinterpret_cast<A*>(la); //la太长,只取低32位0x5678abcd拷贝给pa
    unsigned int u = reinterpret_cast<unsigned int>(pa);//pa逐个比特拷贝到u
    cout << hex << u << endl;  //输出 5678abcd
    
    typedef void (* PF1) (int);
    typedef int (* PF2) (int,char *);
    PF1 pf1;  PF2 pf2;
    pf2 = reinterpret_cast<PF2>(pf1); //两个不同类型的函数指针之间可以互相转换
}

其实reinterpret_cast和普通强制类型转换没多大区别,也不是类型安全的,用reinterpret_cast的好处是:1. 将强制类型转换标准化,代码看着顺眼,而且能快速查找到强制类型转换 2. 不改变参数const属性

reinterpret_cast体现了 C++ 语言的设计思想:用户可以做任何操作,但要为自己的行为负责。

dynamic_cast

dynamic_cast专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 NULL 指针。

能够进行类继承层次之间向下转型或向下转型,除了在编译器进行类型安全检查,dynamic_cast还会在运行时类型检查。
向上转型是无条件的,不会进行任何检测,所以都能成功;向下转型的前提必须是安全的,要借助 RTTI 进行检测,所有只有一部分能成功。
  

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

RTTI

RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。

C++在编译器层面提供了typeiddynamic_cast两个运算符来支持RTTI。
typeid 运算符和type_info类
  

dynamic_cast 和 static_cast

dynamic_cast 是“动态转换”的意思,static_cast 是“静态转换”的意思。dynamic_cast 会在程序运行期间借助 RTTI 进行类型转换,这就要求基类必须包含虚函数;static_cast 在编译期间完成类型转换,能够更加及时地发现错误。

  

单参构造函数的类型转换功能

当一个类定义中提供了单个参数的构造函数时,该类便提供了一种将其它数据类型的数值或变量转换为用户所定义数据类型的方法;因此,可以说,单个参数的构造函数提供了数据类型转换的功能。

C++ 类型转换(static_cast、dynamic_cast、reinterpret_cast、const_cast)_第1张图片在这里插入图片描述
如果类中有单个参数的构造函数,则该构造具有类型转换的作用;编译器在编译过程中会调用单参构造函数将 3 转为 BigNumer 的对象(用explicit修饰构造函数,将会禁止单参构造函数的隐式转换。)

explicit

作用:消除单参构造函数(或有缺省参数的全参构造函数)的类型转换问题;
implicit关键字作用和explicit相反,声明构造函数为隐式调用(默认);

注意:
一、只可以修饰类的构造函数,一般的函数不能修饰。

二、只有类的构造函数有一个参数时候,才有效,当有多个参数时候就没什么意义了(这里有个例外,就是当除了第一个参数以外的其他参数都有默认值的时候,explicit依然有效,此时调用构造函数时只传入一个参数,等效于只有一个参数的类构造函数)

class A{
public :
	A(int num):n(num){}  // 默认为 implicit
private:
	int _a;
};

class B{
public :
	explicit B(int num):n(num){}
private:
	int _b;
};
 
int main()
{
	A a = 12; // 编译通过12会转为A对象赋值,隐式调用构造函数
	B b1(13);  // 编译通过,显示调用构造
	B b2 = 14; // 编译时报错:没有与这些操作数匹配的"="运算符
}

详解 c++ 关键字 explicit


参考:
https://www.jb51.net/article/249183.htm#_label0
http://c.biancheng.net/view/2343.html

你可能感兴趣的:(cpp,c语言,c++,c语言,开发语言,类型转换)