c++ 类型转换

 

来自:https://www.cnblogs.com/TenosDoIt/p/3175217.html

 

c++ 数据类型转换: static_cast dynamic_cast reinterpret_cast const_cast

 

【目录】

引言

static_cast 定义

dynamic_cast 定义

举例:下行转换(把基类的指针或引用转换成子类表示)

举例:上行转换(把子类的指针或引用转换成基类表示)

举例: static_cast 用于基本类型之间、基本类型指针和空指针间的转换

reinterpret_cast 定义

举例:reinterpret_cast用法

const_cast 定义

举例:const_cast用法

总结


 

c语言中我们经常使用类似于 int a =(int)3.14等这种强制类型转换

标准c++的类型转换符:static_cast 、dynamic_cast、 reindivter_cast、 const_cast, 以下分别介绍他们的用法以及举例说明

以下代码编译运行环境:codeblock with gcc in win7(x64)

【1】static_cast 
用法:static_cast < type-id > ( exdivssion ) 

该运算符把exdivssion转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:

①用于类层次结构中基类和子类之间指针或引用的转换。

  进行上行转换(把子类的指针或引用转换成基类表示)是安全的;

  进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。

②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。

③把空指针转换成目标类型的空指针。

④把任何类型的表达式转换成void类型。

注意:static_cast 不能转换掉exdivssion的const、volitale、或者__unaligned属性。

msdn官方解释:http://msdn.microsoft.com/en-us/library/c36yw7x9(v=vs.80).aspx

【2】dynamic_cast

用法:dynamic_cast < type-id > ( exdivssion )

该运算符把exdivssion转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;

如果type-id是类指针类型,那么exdivssion也必须是一个指针,如果type-id是一个引用,那么exdivssion也必须是一个引用。

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。

在类层次间进行上行转换时,dynamic_cast和static_cast 的效果是一样的;

在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast 更安全。

msdn官方解释:http://msdn.microsoft.com/en-us/library/cby9kycs(v=vs.80).aspx

 

举例:下行转换(把基类的指针或引用转换成子类表示)

需要注意的是如果基类中不含虚函数,dynamic_cast 下行转换编译会出错

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 
 4 class father
 5 {
 6 public:
 7     void fun1()
 8     {
 9         cout<<"this is father fun1 call\n";
10     }
11     virtual void fun()
12     {
13         cout<<"this is father fun call\n";
14     }
15 };
16 
17 class son: public father
18 {
19 public:
20     void fun2()
21     {
22         cout<<"this is son fun2 call\n";
23     }
24     void fun()
25     {
26         cout<<"this is the son fun call\n";
27     }
28     int k;
29 };
30 
31 int main()
32 {
33     father *pf, f;
34     son *ps, s;
35 
36     pf = &f;// 基类的指针指向基类对象
37     ps = static_cast<son *>(pf);//这种转换是不安全的,行为是不确定的
38     if(ps != NULL)
39     {
40         ps->fun(); //在本文编译环境下,执行父类的fun
41         //本文编译环境下,一下语句可以执行
42         ps->fun2();
43         ps->k = 1;
44     }
45     ps = dynamic_cast<son *>(pf);//转换后ps = NULL
46     if(ps == NULL)
47         cout<<"dynamic_cast: ps = NULL\n";
48     cout<<"-----------------------------------------------------------------\n";
49     pf = &s; //基类指针开始指向子类对象
50     //此时,两种转换都是安全的
51     ps = static_cast<son *>(pf);
52     if(ps != NULL)
53     {
54         ps->fun();
55         ps->fun2();
56         ps->k = 1;
57     }
58     ps = dynamic_cast<son *>(pf);//转换后ps = NULL
59     if(ps != NULL)
60     {
61         ps->fun();
62         ps->fun2();
63         ps->k = 2;
64     }
65 }
复制代码

结果:

举例:上行转换(把子类的指针或引用转换成基类表示)

复制代码
 1 //类定义同上
 2 int main()
 3 {
 4     father *pf, f;
 5     son *ps, s;
 6 
 7     ps = &s;// 子类的指针指向子类对象
 8     //此时两种转换都是安全的
 9     pf = static_cast<father *>(ps);
10     if(pf != NULL)
11     {
12         pf->fun();
13     }
14     pf = dynamic_cast<father *>(ps);
15     if(pf != NULL)
16     {
17         pf->fun();
18     }
19 
20 }
复制代码

举例: static_cast 用于基本类型之间、基本类型指针和空指针间的转换(不能用于基本类型指针之间转换)。

注意:基本类型由于表示数值范围的不同,因此需要用户保证转换的安全。另外dynamic_cast不能用于此类转换

复制代码
 1 int main()
 2 {
 3     //基本类型间的转换,需要用户保证安全
 4     int a = 1000;
 5     char c = static_cast<char>(a);//不安全,1000超过了char的表示范围
 6     cout<<c<<endl;//输出空
 7     a = 49;
 8     c = static_cast<char>(a);//安全,输出字符‘1’
 9     cout<<c<<endl;
10     //c = dynamic_cast<char>(a); 错误
11     cout<<"-----------------------------------------------------------------\n";
12     //void *和基本类型指针的转换,需要用户保证转换安全
13     a = 49;
14     void *pv;
15     pv = &a;
16     int *pi = static_cast<int *>(pv);//void * 转换为int *
17     cout<<*pi<<endl; //输出49
18     //pi = dynamic_cast<int *>(pv); 错误
19     char *pc = static_cast<char *>(pv);//void *转char*
20     cout<<*pc<<endl;//输出字符‘1’
21     void *pv2 = static_cast<void *>(pc);// char * 转void *
22     cout<<*((char *)pv2)<<endl;////输出字符‘1’
23 }
复制代码

 

【3】reinterpret_cast

用法:reinterpret_cast<type-id> (exdivssion)

reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的比特位。按照reinterpret的字面意思“重新解释”,即对数据的比特位重新解释。

IBM的C++指南 里明确告诉了我们reinterpret_cast可以,或者说应该在什么地方用来作为转换运算符:

  • 从指针类型到一个足够大的整数类型
  • 从整数类型或者枚举类型到指针类型
  • 从一个指向函数的指针到另一个不同类型的指向函数的指针
  • 从一个指向对象的指针到另一个不同类型的指向对象的指针
  • 从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
  • 从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针

总结来说:reinterpret_cast用在任意指针(或引用)类型之间的转换;以及指针与足够大的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型,无视大小。

注意:static_cast 不能转换掉exdivssion的const、volitale、或者__unaligned属性。

msdn官方解释:http://msdn.microsoft.com/en-us/library/e0w9f63b(v=vs.80).aspx

举例:reinterpret_cast用法

复制代码
 1 int main()
 2 {
 3    int a = 49;
 4    int *pi = &a;
 5    char *pc = reinterpret_cast<char*>(pi);//int * 到char *,用户自己安全
 6    cout<<*pc<<endl; //输出字符"1"
 7    unsigned long b = reinterpret_cast<unsigned long>(pc);//char * 转 unsigned long
 8    cout<<b<<endl;//输出pc指向地址(即a的地址)对应的整数
 9    int *pi2 = reinterpret_cast<int *>(b);//unsigned long 转 int*
10    cout<<*pi2<<endl; //输出49
11 }
复制代码

【4】const_cast 

用法:const_cast<type-id> (exdivssion)

该运算符用来修改类型的const、volatile、__unaligned属性。除了const 、volatile、__unaligned修饰之外, type_id和exdivssion的类型是一样的。

常量指针被转化成非常量指针,并且仍然指向原来的对象;

常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。

msdn官方解释:http://msdn.microsoft.com/en-us/library/bz6at95h(v=vs.80).aspx

举例:const_cast用法

复制代码
1 int main()
2 {
3    const int a = 100;
4    int *b = const_cast<int *>(&a);//const int * 转int *
5    cout<<*b<<endl; //输出100
6    cout<<&a<<" "<<b<<endl; //两者值相同,表明b指向a的地址,只是const属性变了
7 }
复制代码


总结:

类指针或引用的上行转换static_cast 和 dynamic_cast 都可以

类指针或引用的下行转换用dynamic_cast并且判断转换后是否为空

基本数据类型之间的转换用static_cast, 但是由于数值范围的不同,需要用户保证转换的安全性

不同类型之间的指针或引用的转换用reinterpret_cast,它的本质是对指向内存的比特位的重解释

消除数据的const、volatile、__unaligned属性,用const_cast

【版权声明】转载请注明出处 http://www.cnblogs.com/TenosDoIt/p/3175217.html

 

=========================================

来自:https://www.cnblogs.com/QG-whz/p/4509710.html

C++强制类型转换操作符 static_cast

 

static_cast是一个强制类型转换操作符。强制类型转换,也称为显式转换,C++中强制类型转换操作符有static_cast、dynamic_cast、const_cast、reinterpert_cast四个。本节介绍static_cast操作符。

 

  • 编译器隐式执行的任何类型转换都可以由static_cast来完成,比如int与float、double与char、enum与int之间的转换等。
double a = 1.999;
int b = static_cast<double>(a); //相当于a = b ;

  当编译器隐式执行类型转换时,大多数的编译器都会给出一个警告:

e:\vs 2010 projects\static_cast\static_cast\static_cast.cpp(11): warning C4244: “初始化”: 从“double”转换到“int”,可能丢失数据

  使用static_cast可以明确告诉编译器,这种损失精度的转换是在知情的情况下进行的,也可以让阅读程序的其他程序员明确你转换的目的而不是由于疏忽。

  把精度大的类型转换为精度小的类型,static_cast使用位截断进行处理。

  • 使用static_cast可以找回存放在void*指针中的值。
    double a = 1.999;
    void * vptr = & a;
    double * dptr = static_cast<double*>(vptr);
    cout<<*dptr<<endl;//输出1.999

static_cast也可以用在于基类与派生类指针或引用类型之间的转换。然而它不做运行时的检查,不如dynamic_cast安全。static_cast仅仅是依靠类型转换语句中提供的信息来进行转换,而dynamic_cast则会遍历整个类继承体系进行类型检查,因此dynamic_cast在执行效率上比static_cast要差一些。现在我们有父类与其派生类如下:

复制代码
class ANIMAL
{
public:
    ANIMAL():_type("ANIMAL"){};
    virtual void OutPutname(){cout<<"ANIMAL";};
private:
    string _type ;
};
class DOG:public ANIMAL
{
public:
    DOG():_name("大黄"),_type("DOG"){};
    void OutPutname(){cout<<_name;};
    void OutPuttype(){cout<<_type;};
private:
    string _name ;
    string _type ;
};
复制代码

此时我们进行派生类与基类类型指针的转换:注意从下向上的转换是安全的,从上向下的转换不一定安全。

复制代码
int main()
{
    //基类指针转为派生类指针,且该基类指针指向基类对象。
    ANIMAL * ani1 = new ANIMAL ;
    DOG * dog1 = static_cast<DOG*>(ani1);
    //dog1->OutPuttype();//错误,在ANIMAL类型指针不能调用方法OutPutType();在运行时出现错误。

    //基类指针转为派生类指针,且该基类指针指向派生类对象
    ANIMAL * ani3 = new DOG;
    DOG* dog3 = static_cast<DOG*>(ani3);
    dog3->OutPutname(); //正确

    //子类指针转为派生类指针
    DOG *dog2= new DOG;
    ANIMAL *ani2 = static_cast<DOG*>(dog2);
    ani2->OutPutname(); //正确,结果输出为大黄

    //
    system("pause");

}
复制代码
  • static_cast可以把任何类型的表达式转换成void类型。
  • static_cast把任何类型的表达式转换成void类型。
  • 另外,与const_cast相比,static_cast不能把换掉变量的const属性,也包括volitale或者__unaligned属性。
   


作者:melonstreet
出处:https://www.cnblogs.com/QG-whz/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

======================================

来自:https://www.cnblogs.com/wanghongyang/p/15054880.html

C++中dynamic_cast与static_cast浅析与实例演示

 

static_cast< new_type >(expression)

备注new_type为目标数据类型,expression为原始数据类型变量或者表达式。

C风格写法:

double scores = 96.5; int n = (int)scores;

C++ 新风格的写法为:

double scores = 96.5; int n = static_cast<int>(scores);

1|21.2 为什么要有static_cast等

隐式类型转换是安全的,显式类型转换是有风险的,C语言之所以增加强制类型转换的语法,就是为了强调风险,让程序员意识到自己在做什么。

但是,这种强调风险的方式还是比较粗放,粒度比较大,它并没有表明存在什么风险,风险程度如何。

为了使潜在风险更加细化,使问题追溯更加方便,使书写格式更加规范,C++ 对类型转换进行了分类,并新增了四个关键字来予以支持,它们分别是:

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

1|31.2 static_cast的作用

static_cast相当于传统的C语言里的强制转换,该运算符把expression转换为new_type类型,用来强迫隐式转换如non-const对象转为const对象,编译时检查,用于非多态的转换,可以转换指针及其他,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:

风险较低的用法:

  • 原有的自动类型转换,例如 short 转 int、int 转 double、const 转非 const、向上转型等;
  • void 指针和具体类型指针之间的转换,例如void *int *char *void *等;
  • 有转换构造函数或者类型转换函数的类与其它类型之间的转换,例如 double 转 Complex(调用转换构造函数)、Complex 转 double(调用类型转换函数)。

需要注意的是,static_cast 不能用于无关类型之间的转换,因为这些转换都是有风险的,例如:

  • 两个具体类型指针之间的转换,例如int *double *Student *int *等。不同类型的数据存储格式不一样,长度也不一样,用 A 类型的指针指向 B 类型的数据后,会按照 A 类型的方式来处理数据:如果是读取操作,可能会得到一堆没有意义的值;如果是写入操作,可能会使 B 类型的数据遭到破坏,当再次以 B 类型的方式读取数据时会得到一堆没有意义的值。
  • int 和指针之间的转换。将一个具体的地址赋值给指针变量是非常危险的,因为该地址上的内存可能没有分配,也可能没有读写权限,恰好是可用内存反而是小概率事件。

1|41.3 static_cast用法

#include <iostream> #include <cstdlib> using namespace std; class Complex{ public: Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ } public: operator double() const { return m_real; } //类型转换函数 private: double m_real; double m_imag; }; int main(){ //下面是正确的用法 int m = 100; Complex c(12.5, 23.8); 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); //不能将整数转换为指针类型 return 0; }

2|02. dynamic_cast

2|12.1 dynamic_cast 语法

dynamic_cast <newType> (expression)

newType 和 expression 必须同时是指针类型或者引用类型。换句话说,dynamic_cast 只能转换指针类型和引用类型,其它类型(int、double、数组、类、结构体等)都不行。

对于指针,如果转换失败将返回 NULL;对于引用,如果转换失败将抛出std::bad_cast异常。

2|22.2 dynamic_cast 用法

dynamic_cast 用于在类的继承层次之间进行类型转换,它既允许向上转型(Upcasting),也允许向下转型(Downcasting)。向上转型是无条件的,不会进行任何检测,所以都能成功;向下转型的前提必须是安全的,要借助 RTTI 进行检测,所有只有一部分能成功。

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

2|32.3 dynamic_cast 实例

2.3.1 向上转型(Upcasting)

向上转型时,只要待转换的两个类型之间存在继承关系,并且基类包含了虚函数(这些信息在编译期间就能确定),就一定能转换成功。因为向上转型始终是安全的,所以 dynamic_cast 不会进行任何运行期间的检查,这个时候的 dynamic_cast 和 static_cast 就没有什么区别了。

向上转型时不执行运行期检测」虽然提高了效率,但也留下了安全隐患,请看下面的代码:

#include <iostream> #include <iomanip> using namespace std; class Base{ public: Base(int a = 0): m_a(a){ } int get_a() const{ return m_a; } virtual void func() const { } protected: int m_a; }; class Derived: public Base{ public: Derived(int a = 0, int b = 0): Base(a), m_b(b){ } int get_b() const { return m_b; } private: int m_b; }; int main(){ //情况① Derived *pd1 = new Derived(35, 78); Base *pb1 = dynamic_cast<Derived*>(pd1); cout<<"pd1 = "<<pd1<<", pb1 = "<<pb1<<endl; cout<<pb1->get_a()<<endl; pb1->func(); //情况② int n = 100; Derived *pd2 = reinterpret_cast<Derived*>(&n); Base *pb2 = dynamic_cast<Base*>(pd2); cout<<"pd2 = "<<pd2<<", pb2 = "<<pb2<<endl; cout<<pb2->get_a()<<endl; //输出一个垃圾值 pb2->func(); //内存错误 return 0; }

运行结果如下

image-20210724114624100

可以看到pd1与pb1的地址相同,且pb1可以正常调用Base类的方法

对于情况②

pd 2指向的是整型变量 n,并没有指向一个 Derived 类的对象,在使用 dynamic_cast 进行类型转换时也没有检查这一点因为向上转型始终是安全的,所以 dynamic_cast 不会进行任何运行期间的检查

而是将 pd 的值直接赋给了 pb(这里并不需要调整偏移量),最终导致 pb 也指向了 n。因为 pb 指向的不是一个对象,所以get_a()得不到 m_a 的值(实际上得到的是一个垃圾值),pb2->func()得不到 func() 函数的正确地址

运行结果如下

image-20210724115237948

image-20210724115309768

简单来说就是向上转型是不检查的,所以大家得知道自己在做什么,不能随意的转换

2.3.2 向下转型(Downcasting)

向下转型是有风险的,dynamic_cast 会借助 RTTI 信息进行检测,确定安全的才能转换成功,否则就转换失败。

下面看一个例子

#include <iostream> using namespace std; class A{ public: virtual void func() const { cout<<"Class A"<<endl; } private: int m_a; }; class B: public A{ public: virtual void func() const { cout<<"Class B"<<endl; } private: int m_b; }; class C: public B{ public: virtual void func() const { cout<<"Class C"<<endl; } private: int m_c; }; class D: public C{ public: virtual void func() const { cout<<"Class D"<<endl; } private: int m_d; }; int main(){ A *pa = new A(); B *pb; C *pc; //情况① pb = dynamic_cast<B*>(pa); //向下转型失败 if(pb == NULL){ cout<<"Downcasting failed: A* to B*"<<endl; }else{ cout<<"Downcasting successfully: A* to B*"<<endl; pb -> func(); } pc = dynamic_cast<C*>(pa); //向下转型失败 if(pc == NULL){ cout<<"Downcasting failed: A* to C*"<<endl; }else{ cout<<"Downcasting successfully: A* to C*"<<endl; pc -> func(); } cout<<"-------------------------"<<endl; //情况② pa = new D(); //向上转型都是允许的 pb = dynamic_cast<B*>(pa); //向下转型成功 if(pb == NULL){ cout<<"Downcasting failed: A* to B*"<<endl; }else{ cout<<"Downcasting successfully: A* to B*"<<endl; pb -> func(); } pc = dynamic_cast<C*>(pa); //向下转型成功 if(pc == NULL){ cout<<"Downcasting failed: A* to C*"<<endl; }else{ cout<<"Downcasting successfully: A* to C*"<<endl; pc -> func(); } return 0; }

运行结果

image-20210724120141187

可以看到,前两次转换失败,但是后两次转换成功

这段代码中类的继承顺序为:A --> B --> C --> D。pa 是A*类型的指针,当 pa 指向 A 类型的对象时,向下转型失败,pa 不能转换为B*C*类型。当 pa 指向 D 类型的对象时,向下转型成功,pa 可以转换为B*C*类型。同样都是向下转型,为什么 pa 指向的对象不同,转换的结果就大相径庭呢?

因为每个类都会在内存中保存一份类型信息,编译器会将存在继承关系的类的类型信息使用指针“连接”起来,从而形成一个继承链(Inheritance Chain),也就是如下图所示的样子:

image-20210724120354068

当使用 dynamic_cast 对指针进行类型转换时,程序会先找到该指针指向的对象,再根据对象找到当前类(指针指向的对象所属的类)的类型信息,并从此节点开始沿着继承链向上遍历,如果找到了要转化的目标类型,那么说明这种转换是安全的,就能够转换成功,如果没有找到要转换的目标类型,那么说明这种转换存在较大的风险,就不能转换。

所以在第二种方式中,pa实际上是指向的D,于是程序顺着D开始向上找,找到了B和C,于是认定是安全的,所以转换成功

总起来说,dynamic_cast 会在程序运行过程中遍历继承链,如果途中遇到了要转换的目标类型,那么就能够转换成功,如果直到继承链的顶点(最顶层的基类)还没有遇到要转换的目标类型,那么就转换失败。对于同一个指针(例如 pa),它指向的对象不同,会导致遍历继承链的起点不一样,途中能够匹配到的类型也不一样,所以相同的类型转换产生了不同的结果。

3|03. 参考链接

http://c.biancheng.net/cpp/biancheng/view/3297.html

https://blog.csdn.net/u014624623/article/details/79837849
https://www.cnblogs.com/wanghongyang/ 【本文博客】

======================

https://blog.51cto.com/u_13030942/5424474

 

 

===========

原文链接:https://blog.csdn.net/Colin_RZL/article/details/103571933

 

1、reinterpret_cast 强制类型转换
使用场景:

不同类型指针之间转换
指针和数字之间转换
函数指针之间转换
void test01()
{
int *ip = nullptr;
// <写上要转换的目标类型>(要转换的变量)
char *cp = reinterpret_cast<char*>(ip);

// 转换为数字类型
int addr = reinterpret_cast<int>(ip);
cout << addr << endl;

// 函数指针转换
typedef void(FUNC_ONE)(void);
typedef void(FUNC_TWO)(int);

FUNC_ONE *fun_ptr1 = nullptr;
FUNC_TWO *fun_ptr2 = reinterpret_cast<FUNC_TWO*>(fun_ptr1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2、const_cast
使用场景:

const 和非 const 指针之间转换
volatile 和非 volatile 指针之间转换
void test02()
{
const int *cip = nullptr;
int *ip = const_cast<int*>(cip);
const int *cip2 = const_cast<const int *>(ip);

// volatile 防止编译器对该变量进行优化. 每次取值时从内存中取.
volatile int *vip = nullptr;
int *ip2 = const_cast<int *>(vip);
}
1
2
3
4
5
6
7
8
9
10
3、static_cast
使用场景:

基础数据类型之间转换
void* 类型和其他类型指针之间转换
具有继承关系的指针引用之间类型转换
// 基类
class Base
{
public:
int m_a;
};

// 派生类
class Sub : public Base
{
public:
int m_b;
int m_c;
};

class Other {};

void test03()
{
// 基础数据类型之间转换
double d = 3.14;
int i = static_cast<int>(d);

int *ip = static_cast<int *>(malloc(4));

Other *o = nullptr;
Sub *s = new Sub;
Base *b = new Base;
// 子类类型指针转换为父类类型指针, 叫做向上类型转换, 是安全的
Base *bb = static_cast<Base *>(s);
// 父类类型指针转换为子类类型指针, 叫做向下类型转换, 是不安全, 需要自己控制
Sub *ss = static_cast<Sub *>(b);
// 向下类型转换取决于指针真正指向的对象
Base *b2 = new Sub;
Sub *ss2 = static_cast<Sub *>(b2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
4、dynimic_cast
使用场景:

多态场景下父子类型指针进行向上、向下类型转换
使用注意:

向上类型转换安全
向下类型转换,要进行动态类型检查
class Animal
{
public:
virtual void func() {}
};

class Dog : public Animal {};

void test04()
{
// 向上类型转换是安全的
Dog *d = new Dog;
Animal *a = dynamic_cast<Animal *>(d);

// 向下类型转换
// 编译器如何检测向下类型转换是否安全?
// 如果是安全的,返回转换后的指针
// 如果是不安全的,返回空指针
// 为什么 dynamic_cast 向下类型转换时候必须需要多态呢?
// 靠多态机制产生虚函数表,编译器将对象类型信息存储在虚函数表
// 当发生转换的时候,就可以确定指针指向的对象类型了
Animal *aa = new Animal;
Dog *dd = dynamic_cast<Dog *>(aa);
cout << (int)dd << endl;

Animal *bb = new Dog;
Dog *dd2 = dynamic_cast<Dog *>(bb);
cout << (int)dd2 << endl;

}
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/Colin_RZL/article/details/103571933

 

==

#include <iostream>
#include <string>
using namespace std;


class Animal {
public:
    void eat(){cout << "animal eat"<< endl;}
    virtual void name (){cout << "animal name" << endl;}
};

class Dog : public Animal{
public:
    Dog():m_type("dog"){}
    void eat() {cout << "dog eat" << endl;}
    void name () {cout << "dog animal " << endl;}
    void bark(){cout << "dog bark"<< endl;}
    void outputtype(){cout << "type:" << m_type << endl;}
    string m_type;
};
int main(){

    int a = 67;
    double d = 1.99;
    a = 67;

    char c;
    c = static_cast<char> (a);

    cout << "c:" << c << endl;

    a = static_cast<int> (d);
    cout << "a:" << a << endl;

    void * vptr =&d;
    double *ptr_double =static_cast<double*>(vptr);
    cout << "ptr_double:" << ptr_double << "  *ptr_double: "<< *ptr_double << endl;

    vptr = static_cast<void*>(ptr_double);

    Animal* ani= new Animal;
    Dog* dog = new Dog;


    Animal * ani1 = static_cast<Animal*> (dog);
    Dog * dog1 = static_cast<Dog*> (ani);
    
    ani1->eat();
    ani1->name();

    dog1->eat();
    dog1->name();
    dog1->bark();
    // dog1->outputtype(); // 运行出错 
    
    // dynamic_cast
    Dog* dog2 = dynamic_cast<Dog*>(ani);
    if (dog2==nullptr){
        cout <<"dynamic error" << endl;
    }else{
    
    dog2->outputtype();
    dog2->bark();
    }

    // const_cast
    const int b =69;
    const int *pi = &b;
    int *pi1 = const_cast<int*> (pi);


    // reinterpret
    int a1 = 70;
    int *pa1 = &a1;
    int *pa2 = nullptr;
    char * pc1 = reinterpret_cast<char*> (pa1);
    long long  a2 = reinterpret_cast<long long> (pa2);
    long long  a3 = reinterpret_cast<long long> (pa1);
    
    cout << "a3:" << a3 << " pa1:" << pa1 << " a2:"  << a2 <<endl;

    cout << "hello " << endl;
    return 0;
}

 

============

在C++编程中,类型转换是一项基础而强大的特性,允许开发者在不同数据类型间转换和操作数据。本文将详细分析C++的四种类型转换:static_cast、reinterpret_cast、dynamic_cast 和 const_cast。通过精心设计的示例和深入讲解。

强调理解这些转换只是改变了编译器对地址解释的方式,而非创建或转换实际对象。加深您对C++类型转换的理解

 

1. static_cast<void*>()静态转换

static_cast 是一种更安全的转换方式,它在编译时检查转换的合法性。它通常用于基类和派生类指针之间的转换,或者具有相关类型转换操作符的类。

代码位置:deps\oblib\src\lib\hash\ob_hash.h

 // 获取下一个节点的指针,未标记删除的节点
    Node *get_next()
    {
      return static_cast<Node *>(DMark::unset(ATOMIC_LOAD(&next_)));
    }

举个例子:

其中参数d 是一个Derived *d = new Derived(); 类型;和自定义类Student 完全没有关系。

这种情况下 reinterpret_cast 照样可以转换,并且还可以运行,运行后还可以输出。正常创建了 student这个对象。

但是 static_cast 在编译阶段就提示出来了,不合法。

 

static_cast和 reinterpret_cast的几种使用场景

static_cast 主要用于以下几种情形:

    • 在相关类型之间进行转换,比如整数类型和浮点类型之间,或者将 void* 转换为具体类型的指针。
    • 在类层次结构中向上转型(从子类指针或引用转换为父类指针或引用),这种转换在类层次结构中是安全的。
    • 向下转型(从父类指针或引用转换为子类指针或引用),但这要求转换是合法的,即实际的对象类型确实是目标子类类型。如果不是,结果是未定义的。

 

reinterpret_cast 主要用于以下几种情形:

    • 用于任意指针(或引用)类型之间的转换,但不检查安全性。它只是告诉编译器把指针从一种类型重新解释为另一种类型。

 

 

2. reinterpret_cast<void *>()重新解释转换

re:再次
interpret:解释
cast:转换

reinterpret_cast 是 C++ 中的一个类型转换运算符,它用于执行低级别的、不安全的类型转换。与其他类型转换运算符(如 static_castdynamic_cast 和 const_cast)相比,reinterpret_cast 提供了最少的类型检查。它基本上可以将任何指针类型转换为任何其他指针类型,甚至可以将指针转换为足够大的整数类型,反之亦然。

reinterpret_cast 通常用于以下场景:

  1. 当你需要将一种指针类型转换为完全不同的指针类型时。
  2. 当你需要将指针和足够大的整数类型之间进行转换时。
  3. 当你需要操作指针的二进制表示时,例如清除指针的某些位。【这个也挺重要】

A:指针与整数之间的转换:

    void* ptr= reinterpret_cast<void*>(0xdeadbeef); // 将整数转换为void*指针
    std::uintptr_t addr= reinterpret_cast<std::uintptr_t>(ptr);  // 将void*指针转换为整数
    std::cout<<"ptr:"<<ptr<<std::endl;
    std::cout<<"node:"<<addr<<std::endl;

 

 

B:指针之间的转换

一般都是相关类型

// 转换相关代码

#include <iostream>

class Base
{
public:
    Base()
    {
    }
    int data = 0;
};
class Derived : public Base
{

public:
    Derived()
    {
    }
    int value = 10;
};

int main()
{
    Base *b = new Base();
    b->data = 100;
    std::cout << "b->data:" << b->data << std::endl;
    Derived *d = new Derived();
    d->data = 200;
    d->value = 100;
    std::cout << "d->data:" << d->data << std::endl;
    std::cout << "d->value:" << d->value << std::endl;

    //--将父类 转换为子类。 可以调用其成员。但是缺无法正常访问到数据
    Derived *d1 = reinterpret_cast<Derived *>(b);
    std::cout << "d1->data:" << d1->data << std::endl; //输出正常
    std::cout << "d1->value:" << d1->value << std::endl; //输出 0

    //--将子类 转换父类。 
    Base *b1=reinterpret_cast<Base*>(d);
    std::cout << "b1->data:" << b1->data << std::endl; //输出正常
    return 0;
}

即使从 Derived* 到 Base* 的转换可以安全地使用 static_cast 完成(因为存在继承关系),依然可以使用 reinterpret_cast 来强制执行,但是不推荐这么做。

 

 

C:不相关类型的指针之间的转换

int* iptr = new int(42);
// 转换 int* 到 char*,可能为了操作内存块的单个字节
char* cptr = reinterpret_cast<char*>(iptr);

// 修改 int 值的第一个字节
*cptr = 'A';

 

D:函数指针之间的转换:

typedef void (*FuncPtr)();
void someFunction() {}

FuncPtr funcPtr = reinterpret_cast<FuncPtr>(&someFunction);

3. const_cast 常量转换

const_cast 是C++中用来移除变量的 const 性质的类型转换操作符。它的典型用途包括:

  • 1. 在有const 和 非const版本的重载函数中进行转换。
  • 2. 当你需要向一个只接受非 const指针或者引用的函数传递 const数据时。【可以用这个转换下】
  • 3. 当需要修改由const定义 但实际上不应该是 const 的遗留代码的数据时。
void print(char *str) {
    std::cout << str << std::endl;
}

int main() {
    const char *cstr = "Hello World";
    // print(cstr); // 错误:不能从const char*转换为char*
    print(const_cast<char *>(cstr)); //

 


4. dynamic_cast 动态转换

dynamic_cast 是 C++中的一个类型转换操作符,它主要用于处理多态类型的安全向下转换(也就是父类向子类转换)。 如果转换不合法,对于指针类型,dynamic_cast 会返回空指针 nullptr ; 对于引用类型,它会抛出 std::bda_cast 异常

注意:dynamic_cast是在运行时检查,并且 使用dynamic_cast 转换时,涉及的类通常至少需要有一个虚函数(比如虚析构函数),这样编译器才能再运行时使用类型信息和执行转换。
另外在转换前,也得 Base* base =new Derived1; 指向这个对象,不然会存在 和reinterpret_cast一样的问题

下面是一个简单例子:

 

#include <iostream>

// 基类
class Base {
public:
    virtual ~Base() {}   // 虚析构函数
    virtual void print() const {
        std::cout << "Base class\n";
    }
};

// 派生类1
class Derived1 : public Base {
public:
    void print() const override {
        std::cout << "Derived1 class\n";
    }

    void derived1SpecificFunction() {
        std::cout << "Derived1 specific function\n";
    }
};

// 派生类2
class Derived2 : public Base {
public:
    void print() const override {
        std::cout << "Derived2 class\n";
    }
};

int main() {
    // 创建一个 Derived1 对象,但是以 Base 类型指针存储
    Base* base = new Derived1;

    // 现在,我们想要将 base 指针转换为 Derived1 类型的指针
    Derived1* derived1 = dynamic_cast<Derived1*>(base);

    if (derived1 != nullptr) {
        // 转换成功,调用 Derived1 类的函数
        derived1->derived1SpecificFunction();
    } else {
        // 转换失败,可能是 base 实际上指向其他派生类的实例
        std::cout << "Conversion to Derived1* failed.\n";
    }

    // 尝试转换到 Derived2 类型,应该失败
    Derived2* derived2 = dynamic_cast<Derived2*>(base);

    if (derived2 != nullptr) {
        // 这段代码不应该被执行,除非 base 真的指向 Derived2 的实例
        std::cout << "Conversion to Derived2* worked (unexpected).\n";
    } else {
        std::cout << "Conversion to Derived2* failed (expected).\n";
    }

    // 最后别忘了释放内存
    delete base;

    return 0;
}

 

 

5. 重点-深入理解下reinterpret_cast的转换原理?

在内存级别,两个指针无非是不通的内存地址,而内存地址也可以理解为是一个数字,一个编码。

那这跟类型有何关系呢? 其实这个转换本身跟类型也没关系, 只是从抽象层面理解成了类型转换。

reinterpret_cast 在C++是最不安全的类型转换操作符,有点像强制转换。它允许开发者将任意类型的指针转换为几乎任何其他类型的指针。
比如下面这段代码执行的时候,一样是可以的:
// 一个Student *s   转换为 Derived *d   那这个类型信息是怎么出来的?

Derived *d = new Derived();
Student *st=reinterpret_cast<Student*>(d);
std::cout << "st->getData():" << st->getData() << std::endl;
// 这个可以正常输出。

 

上面的代码-你实际上在做的是告诉编译器:

  • ---》“我知道 d 可能不是一个 student类型的指针,但是我就想把它当作是一个 student 类型的指针。 ”
  • ---》这个转换操作并不会把 改变 d 指针的值,也不会为 student 对象分配内存,更不会在堆栈上创建任何东西。它仅仅告诉编译器 如何理解哪块内存。
如果 d 实际上并不是一个 Student 对象的指针,那么通过 st 访问 getData() 时可能发生以下情况:
  1. 程序崩溃(segmentation fault):如果 d 指向的内存区域不可访问或者 getData() 方法访问了无效的内存区域。
  2. 未定义的行为:如果 d 指向的内存区域包含随机数据或与 Student 类型不兼容的数据,getData() 的返回值可能是随机的或意料之外的。
  3. 看似“正常”的输出:即使 d 指向的内存并不是一个 Student 对象,它的内存布局恰好使得 getData() 调用返回了一个看似合理的值。这完全是偶然的,并不能说明代码是安全或正确的。

 

下面是类的代码,上面用到class。

class Base
{
public:
    Base()
    {
    }
    int data = 0;
};
class Derived : public Base
{

public:
    Derived()
    {
    }
    int value = 10;
};

class Student{

    public:
      int getData(){
        return 190;
      }
};

 

重要的是要知道,仅仅因为程序没有崩溃并且输出了某些值,并不意味着这种类型转换是安全或正确的。

来自:https://zhuanlan.zhihu.com/p/679500619

========================

参考:

 

posted @ 2024-05-31 09:24  redrobot  阅读(11)  评论(0编辑  收藏  举报