5.C++里的4种新型类型转换

1首先来回顾C的强制转换

大家都知道,在编译C语言中的强制转换时,编译器不会检查转换是否成功,都会编译正确.

比如:

#include "stdio.h"

struct Position
{
int x;
int y;
};

int main()
{
 int i;
 struct Position *p;

 i=0x123456;

 p=(struct Position *)i;

 printf("px=%d,py=%d\n",p->x,p->y);
}

 输出结果如下图所示:

 

从上图可以看到,只有当运行代码时,才会出现段错误问题.

C代码上千行,若出现这种问题,是非常难找的.

 

2.C++的新型类型转换

所以在C++,便引入了4强制类型转换

PS:如果从非void*转到void*,则不需要强转.比如:

 

  void* pVoid1= const_cast<char*>("123456"); //char* 转为 void*

  int d = 2;
    
  void* pVoid2= &d;       //int* 转为 void*

 

 

2.1 static_cast(静态类型转换)

  • 用于基本数据类型以及对象之间的转换(char,int,const int)
  • 不能用于基本数据类型指针之间的转换(char *,int *)
  • 用于有继承关系类对象指针之间的转换
  • 用于类指针之间的转换
  • 可以用于void*转换

示例-基本数据:

    int i = 0x45;

    char c = 'c';

    c = static_cast<char>(i);    
    //char* pc = static_cast<char*>(&i);   //此行错误,不能用于基本指针之间转换

示例-基本数据与对象转换:

class Test{

public: 
        explicit Test(int i)     //只能显示调用
        {
            cout<<i<<endl; 
        } 
}; 
int main()
{
    Test t = static_cast<Test>(3.55);   //等价于 : Test t(3);
} 

示例-有继承关系的类对象指针转换:

class Parent
{
public:
        int mm;
        Parent(int i)
        {
            mm=i;
            cout<<"Parent:"<<i<<endl;     
        } 

    
}; 
 
class Child : public Parent  
{
public:
        int mval; 
        Child(int i):Parent(i)
        {
            mval=i; 
            cout<<"Child:"<<i<<endl;
        } 
};

int main()
{
     Parent *p =new Parent(3);                   //会调用父类构造函数
     Child  *c = static_cast <Child *> (p) ;     //并不会调用子类构造函数,此时的mval成员为随机值
     c->mval=100;
     cout<<"mval:"<<c->mval<<endl;

    cout<<"mm:"<<c->mm<<endl;          //此时的c->mm指向的对象就是p->mm
    c->mm=100;                   //修改c->mm 等价于修改p->mm
    cout<<"mm:"<<p->mm<<endl;


}                   

运行打印:

Parent:3
mval:100
mm:3
mm:100

 示例-void*转换:

void funcTest(void* pArgs)
{
     unsigned char data = *static_cast<unsigned char*>(pArgs);
     printf("%d",data);
    
}

int main()
{
   unsigned char c =48   
    
   funcTest(&c);
}

 

2.2 const_cast(去常类型转换)

  • 常用于去除const类对象只读属性
  • 且强制转换的类型必须是指针*引用&
  • 对于一串字符,则需要使用const_cast

 

示例1:

const int x =1;     //const:定义一个常量x 

const int& j =2;    //const引用:定义一个只读变量j

int& p1= const_cast<int&>(x);   //强制转换int &

int *p2 = const_cast<int*>(&j);  //强制转换int*

//int p3 = const_cast<int>(j);    //此行错误,不能转换普通数据型

char* p4 = const_cast<char*>("nuo qian"); //对于字符串常量,也只能先使用const_cast来去常
p1=3; *p2=4; printf("x=%d, j=%d\n",x,j); printf("p1=%d *p2=%d\n",p1,*p2);

输出结果:

x=1   j=4

p1=3  *p2=4

从输出结果,可以看出修改p1,p2,只有j内容变换了,是因为变量jconst引用定义的,所以是个只读变量.

 

示例2-去除const类对象的只读属性

class Test 
{
public:
        int mval; 
        Test():mval(10)
        {
            
        }
}; 

int main()
{
     const Test n1;
     
     //n1.mval = 100;  //error,不能直接修改常量对象的成员
     
     Test *n2 =  const_cast<Test *>(&n1);    //通过指针*转换 
     Test &n3 =  const_cast<Test &>(n1);    //通过引用&转换 
      
     n2->mval = 20;
     
     cout<<n1.mval<<endl;        //打印20
     
     n3.mval = 30;
     
     cout<<n1.mval<<endl;        //打印30
}   

 示例3-配合reinterpret_ cast将const指针变量转为其它类型变量

    const char constC = '1';

    //首先去常,得到char*,然后转为uchar*
    uchar* val = reinterpret_cast<unsigned char*>( const_cast<char*>(&constC) );  // *val = 48
typedef struct {
    int u16SigId;
} TagRecvMsg;

typedef struct {
    int u16SigId;
} TagRecvMsg2;

const TagRecvMsg msgList[] = { {10},{16},{18} };


 TagRecvMsg2* val = reinterpret_cast< TagRecvMsg2*>( const_cast<TagRecvMsg*>(msgList));
//对于数组之内的,则先取常,在reinterpret

指针数组之间的转换:

    const char list[][20] = { "123456", "99999" };  //二维数组
    const char * list2[] = {    //指针数组,由于[]优先级高,所以先表示数组,最后表示是指针,所以内容都是指向静态字符串的地址
        "Home", "首页",
        "Monitoring","实时监控",
        "Query", "历史查询"
    }; 
    char* val = const_cast< char*>( reinterpret_cast<const char*>(list+1)); //转为指针,val="99999"
    char** pBuf2 = const_cast<char **>(&list2[2]);  //转为指针数组,val1[0]="Monitoring"、val1[0]="实时监控"
    //让pBuf2 = &list2[2],则*pBuf2(也就是pBuf2[0])就等于list2[2],等于"Monitoring"
    cout<<val<<","<<pBuf2[0];

 

2.3 dynamic_cast(动态类型转换)

  • 用于有继承关系的类指针(引用)间的转换
  • 用于有交叉关系的类指针(引用)间的转换
  • 具有类型检查的功能,编译时会去检查使用的方法是否正确,转换是否成功只有在程序运行时才能知道
  • 类中必须有虚函数的支持
  • 不能用于基本数据类型指针之间的转换(char *,int *)

-当转换为指针时:

  •  转换成功  : 得到目标类型的指针
  •  转换失败  : 得到一个空指针

-当转换为引用时:

  •  转换成功  : 得到目标类型的引用
  •  转换失败  : 得到一个异常操作信息

 

示例-通过子类指针去指向父类:

#include <iostream>
using namespace std;

class Base
{
public:
    Base()
    {
        cout << "Base::Base()" << endl;
    }

    virtual void func()
    {
        cout << "Base::func()" << endl;
    }

    virtual ~Base()
    {
        cout << "Base::~Base()" << endl;
    }

};

class Derived : public Base
{
public:
    virtual void func()
    {
        cout << "Derived::func()" << endl;
    }

};

int main()
{
    Base* p = new Base;                            //初始化父类指针

    Derived* pd = dynamic_cast<Derived*>(p);    //由于父类指针指向的是父类,只有父类虚函数表,没有子类的虚函数表,所以转换失败
                             //如果使用static_cast或者reinterpret_cast,则能成功,调用pd->func则打印Base::func()    
  
//pd->func(); //由于pd=0,所以这里会程序崩溃 cout << "pd = " << pd << endl; //转换失败,打印 0 delete p; p = reinterpret_cast<Base*>(new Derived); //等价于p = (Base*)new Derived; 或者 p = reinterpret_cast<Base*>(new Derived)
                              //或者static_cast
pd = dynamic_cast<Derived*>(p); //由于父类指针指向的是子类,所以有子类虚函数表 pd->func(); //打印"Derived::func()" cout <<"pd = " << pd <<endl; //转换成功,打印地址值 delete p; return 0; }

 所以:

父类(无虚函数)强制转为子类时,可以用static_cast或者reinterpret_cast进行强转.

如果子类强转为父类时,可以用 隐式转换

如果是父类(有虚函数)转为子类,则要使用dynamic_cast

 示例-通过dynamic_cast来检查子类是DerivedA还是DerivedB:

class Base
{
public:

    virtual void func()
    {
         cout<< "Base func" << endl;
    }

};


class DerivedA :  public Base
{
public:
    virtual void func()
    {
         cout<< "DerivedA func" << endl;
    }
};

class DerivedB :  public Base
{
public:
    virtual void func()
    {
         cout<< "DerivedB func" << endl;
    }
};


Base* creat(bool  isA)
{
    if(isA)
        return new DerivedA;
    else
        return new DerivedB;
}

int main()
{

    Base* base = creat(false);

    cout<<dynamic_cast<DerivedA*>(base)<<endl;    //等于0,dynamic_cast具有类型检查,因为base本质是DerivedB,从而能判断出是哪个类

    cout<<dynamic_cast<DerivedB*>(base)<<endl;    //等于0x7c1418,因为base本质是DerivedB

    cout<<(DerivedA*)(base)<<endl;         //等于0x7c1418,因为强制转换不支持虚函数检查

    ((DerivedA*)(base))->func();          //由于本质是DerivedB,所以打印DerivedB,这里不能写成(DerivedA*)(base)->func(),必须得有个括号

    cout<<(DerivedB*)(base)<<endl;       //等于0x7c1418,因为base本质是DerivedB

    cout<<reinterpret_cast<DerivedA*>(base)<<endl;    //等于0x7c1418,因为reinterpret_cast不做具有类型检查

    cout<<reinterpret_cast<DerivedA*>(base)<<endl;     //等于0x7c1418,因为reinterpret_cast不做具有类型检查


    return 0;

}

 

 示例-通过多重继承下的类指针转换: 

class BaseA
{
public:
    virtual void funcA()
    {
        cout<<"BaseA: funcA()"<<endl;
    }
    
}; 
class BaseB { public: virtual void funcB() { cout<<"BaseB: funcB()"<<endl; } };
class Derived : public BaseA,public BaseB { };
int main() { Derived d; BaseA *pa=&d; pa->funcA(); //打印 BaseA: funcA() /*通过强制转换执行*/ BaseB *pb=(BaseB *)pa; pb->funcB(); //还是打印 BaseA: funcA(), 因为pb还是指向pa,执行的还是pa的虚函数表 /*通过dynamic_cast执行*/ pb = dynamic_cast<BaseB *>(pa); pb->funcB(); //打印 BaseB: funcB() //编译器会去检测pa所在的地址,发现有多个虚函数表,然后根据 <BaseB *>来修正指针pb return 0; }

  

 

2.4 reinterpret_ cast(解读类型转换)

  • 用于所有指针的强制转换

(解读是指:对要转换的数据进行重新的解读)

例如:

    int i = 0;

    char j='c';
int   *p1=reinterpret_cast<int *>(&i);

    char  *p2=reinterpret_cast<char *>(&j);

    //int  p3=reinterpret_cast<int >i;  //此行错误,不能转换普通数据型

示例-对指针引用进行强转:

struct EMAP_MSG_S
{
unsigned short msg;
unsigned short id;
char buf[100];
};


EMAP_MSG_S g_msg={
    0xFF,           //2字节
    0x1575,         //2字节
    "hello test"
};

//由于&优先级高,所以说明是个引用,其次表示是个指针类型,所以pRecvMessage是个指向指针的引用
void func(EMAP_MSG_S *&pRecvMessage)
{
    cout<<"buf:"<<pRecvMessage->buf<<endl;

    char *pBuf = reinterpret_cast<char *>(pRecvMessage) + 4;
    //偏移4字节,所以指向buf,等价于 VOS_CHAR *pBuf = (char *)(pRecvMessage) + 4;

    cout<<"pBuf:"<<pBuf<<endl;
}

int main()
{
    EMAP_MSG_S *pRecvMessage = &g_msg;

    func(pRecvMessage); //所以这里参数需要填入指针.
    return 0;

}

打印:

 

 

 

 

 

 

    

 

 

------------恢复内容开始------------

1首先来回顾C的强制转换

大家都知道,在编译C语言中的强制转换时,编译器不会检查转换是否成功,都会编译正确.

比如:

#include "stdio.h"

struct Position
{
int x;
int y;
};

int main()
{
 int i;
 struct Position *p;

 i=0x123456;

 p=(struct Position *)i;

 printf("px=%d,py=%d\n",p->x,p->y);
}

 输出结果如下图所示:

 

从上图可以看到,只有当运行代码时,才会出现段错误问题.

C代码上千行,若出现这种问题,是非常难找的.

 

2.C++的新型类型转换

所以在C++,便引入了4强制类型转换

PS:如果从非void*转到void*,则不需要强转.比如:

  void* pVoid1= const_cast<char*>("123456"); //char* 转为 void*

  int d = 2;
    
  void* pVoid2= &d;       //int* 转为 void*

 

2.1 static_cast(静态类型转换)

  • 用于基本数据类型以及对象之间的转换(char,int,const int)
  • 不能用于基本数据类型指针之间的转换(char *,int *)
  • 用于有继承关系类对象指针之间的转换
  • 用于类指针之间的转换
  • 可以用于void*转换

示例-基本数据:

    int i = 0x45;

    char c = 'c';

    c = static_cast<char>(i);    
    //char* pc = static_cast<char*>(&i);   //此行错误,不能用于基本指针之间转换

示例-基本数据与对象转换:

class Test{

public: 
        explicit Test(int i)     //只能显示调用
        {
            cout<<i<<endl; 
        } 
}; 
int main()
{
    Test t = static_cast<Test>(3.55);   //等价于 : Test t(3);
} 

示例-有继承关系的类对象指针转换:

class Parent
{
public:
        int mm;
        Parent(int i)
        {
            mm=i;
            cout<<"Parent:"<<i<<endl;     
        } 

    
}; 
 
class Child : public Parent  
{
public:
        int mval; 
        Child(int i):Parent(i)
        {
            mval=i; 
            cout<<"Child:"<<i<<endl;
        } 
};

int main()
{
     Parent *p =new Parent(3);                   //会调用父类构造函数
     Child  *c = static_cast <Child *> (p) ;     //并不会调用子类构造函数,此时的mval成员为随机值
     c->mval=100;
     cout<<"mval:"<<c->mval<<endl;

    cout<<"mm:"<<c->mm<<endl;          //此时的c->mm指向的对象就是p->mm
    c->mm=100;                   //修改c->mm 等价于修改p->mm
    cout<<"mm:"<<p->mm<<endl;


}                   

运行打印:

Parent:3
mval:100
mm:3
mm:100

 示例-void*转换:

void funcTest(void* pArgs)
{
     unsigned char data = *static_cast<unsigned char*>(pArgs);
     printf("%d",data);
    
}

int main()
{
   unsigned char c =48   
    
   funcTest(&c);
}

 

2.2 const_cast(去常类型转换)

  • 常用于去除const类对象只读属性
  • 且强制转换的类型必须是指针*引用&
  • 对于一串字符,则需要使用const_cast

示例1:

const int x =1;     //const:定义一个常量x 

const int& j =2;    //const引用:定义一个只读变量j

int& p1= const_cast<int&>(x);   //强制转换int &

int *p2 = const_cast<int*>(&j);  //强制转换int*

//int p3 = const_cast<int>(j);    //此行错误,不能转换普通数据型

char* p4 = const_cast<char*>("nuo qian"); //对于字符串常量,也只能先使用const_cast来去常
p1=3; *p2=4; printf("x=%d, j=%d\n",x,j); printf("p1=%d *p2=%d\n",p1,*p2);

输出结果:

x=1   j=4

p1=3  *p2=4

从输出结果,可以看出修改p1,p2,只有j内容变换了,是因为变量jconst引用定义的,所以是个只读变量.

 

示例2-去除const类对象的只读属性

class Test 
{
public:
        int mval; 
        Test():mval(10)
        {
            
        }
}; 

int main()
{
     const Test n1;
     
     //n1.mval = 100;  //error,不能直接修改常量对象的成员
     
     Test *n2 =  const_cast<Test *>(&n1);    //通过指针*转换 
     Test &n3 =  const_cast<Test &>(n1);    //通过引用&转换 
      
     n2->mval = 20;
     
     cout<<n1.mval<<endl;        //打印20
     
     n3.mval = 30;
     
     cout<<n1.mval<<endl;        //打印30
}   

 示例3-配合reinterpret_ cast将const指针变量转为其它类型变量

    const char constC = '1';

    //首先去常,得到char*,然后转为uchar*
    uchar* val = reinterpret_cast<unsigned char*>( const_cast<char*>(&constC) );  // *val = 48
typedef struct {
    int u16SigId;
} TagRecvMsg;

typedef struct {
    int u16SigId;
} TagRecvMsg2;

const TagRecvMsg msgList[] = { {10},{16},{18} };


 TagRecvMsg2* val = reinterpret_cast< TagRecvMsg2*>( const_cast<TagRecvMsg*>(msgList));
//对于数组之内的,则先取常,在reinterpret

指针数组之间的转换:

    const char list[][20] = { "123456", "99999" };  //二维数组
    const char * list2[] = {    //指针数组,由于[]优先级高,所以先表示数组,最后表示是指针,所以内容都是指向静态字符串的地址
        "Home", "首页",
        "Monitoring","实时监控",
        "Query", "历史查询"
    }; 
    char* val = const_cast< char*>( reinterpret_cast<const char*>(list+1)); //转为指针,val="99999"
    char** pBuf2 = const_cast<char **>(&list2[2]);  //转为指针数组,val1[0]="Monitoring"、val1[0]="实时监控"
    //让pBuf2 = &list2[2],则*pBuf2(也就是pBuf2[0])就等于list2[2],等于"Monitoring"
    cout<<val<<","<<pBuf2[0];

 

2.3 dynamic_cast(动态类型转换)

  • 用于有继承关系的类指针(引用)间的转换
  • 用于有交叉关系的类指针(引用)间的转换
  • 具有类型检查的功能,编译时会去检查使用的方法是否正确,转换是否成功只有在程序运行时才能知道
  • 类中必须有虚函数的支持
  • 不能用于基本数据类型指针之间的转换(char *,int *)

-当转换为指针时:

  •  转换成功  : 得到目标类型的指针
  •  转换失败  : 得到一个空指针

-当转换为引用时:

  •  转换成功  : 得到目标类型的引用
  •  转换失败  : 得到一个异常操作信息

 

示例-通过子类指针去指向父类:

#include <iostream>
using namespace std;

class Base
{
public:
    Base()
    {
        cout << "Base::Base()" << endl;
    }

    virtual void func()
    {
        cout << "Base::func()" << endl;
    }

    virtual ~Base()
    {
        cout << "Base::~Base()" << endl;
    }

};

class Derived : public Base
{
public:
    virtual void func()
    {
        cout << "Derived::func()" << endl;
    }

};

int main()
{
    Base* p = new Base;                            //初始化父类指针

    Derived* pd = dynamic_cast<Derived*>(p);    //由于父类指针指向的是父类,只有父类虚函数表,没有子类的虚函数表,所以转换失败
                             //如果使用static_cast或者reinterpret_cast,则能成功,调用pd->func则打印Base::func()    
  
//pd->func(); //由于pd=0,所以这里会程序崩溃 cout << "pd = " << pd << endl; //转换失败,打印 0 delete p; p = reinterpret_cast<Base*>(new Derived); //等价于p = (Base*)new Derived; 或者 p = reinterpret_cast<Base*>(new Derived)
                              //或者static_cast
pd = dynamic_cast<Derived*>(p); //由于父类指针指向的是子类,所以有子类虚函数表 pd->func(); //打印"Derived::func()" cout <<"pd = " << pd <<endl; //转换成功,打印地址值 delete p; return 0; }

 所以:

父类本身(没有虚函数)转为子类时,可以用static_cast或者reinterpret_cast进行强转.

如果子类强转为父类时,可以用 隐式转换 .

如果是父类(有虚函数)转为子类,则要使用dynamic_cast

 示例-通过dynamic_cast来检查子类是DerivedA还是DerivedB:

class Base
{
public:

    virtual void func()
    {
         cout<< "Base func" << endl;
    }

};


class DerivedA :  public Base
{
public:
    virtual void func()
    {
         cout<< "DerivedA func" << endl;
    }
};

class DerivedB :  public Base
{
public:
    virtual void func()
    {
         cout<< "DerivedB func" << endl;
    }
};


Base* creat(bool  isA)
{
    if(isA)
        return new DerivedA;
    else
        return new DerivedB;
}

int main()
{

    Base* base = creat(false);

    cout<<dynamic_cast<DerivedA*>(base)<<endl;    //等于0,dynamic_cast具有类型检查,因为base本质是DerivedB,从而能判断出是哪个类

    cout<<dynamic_cast<DerivedB*>(base)<<endl;    //等于0x7c1418,因为base本质是DerivedB

    cout<<(DerivedA*)(base)<<endl;         //等于0x7c1418,因为强制转换不支持虚函数检查

    ((DerivedA*)(base))->func();          //由于本质是DerivedB,所以打印DerivedB,这里不能写成(DerivedA*)(base)->func(),必须得有个括号

    cout<<(DerivedB*)(base)<<endl;       //等于0x7c1418,因为base本质是DerivedB

    cout<<reinterpret_cast<DerivedA*>(base)<<endl;    //等于0x7c1418,因为reinterpret_cast不做具有类型检查

    return 0;

}

 

 示例-通过多重继承下的类指针转换: 

class BaseA
{
public:
    virtual void funcA()
    {
        cout<<"BaseA: funcA()"<<endl;
    }
    
}; 
class BaseB { public: virtual void funcB() { cout<<"BaseB: funcB()"<<endl; } };
class Derived : public BaseA,public BaseB { };
int main() { Derived d; BaseA *pa=&d; pa->funcA(); //打印 BaseA: funcA() /*通过强制转换执行*/ BaseB *pb=(BaseB *)pa; pb->funcB(); //还是打印 BaseA: funcA(), 因为pb还是指向pa,执行的还是pa的虚函数表 /*通过dynamic_cast执行*/ pb = dynamic_cast<BaseB *>(pa); pb->funcB(); //打印 BaseB: funcB() //编译器会去检测pa所在的地址,发现有多个虚函数表,然后根据 <BaseB *>来修正指针pb return 0; }

  

 

2.4 reinterpret_ cast(解读类型转换)

  • 用于所有指针的强制转换

(解读是指:对要转换的数据进行重新的解读)

例如:

    int i = 0;

    char j='c';
int   *p1=reinterpret_cast<int *>(&i);

    char  *p2=reinterpret_cast<char *>(&j);

    //int  p3=reinterpret_cast<int >i;  //此行错误,不能转换普通数据型

示例-对指针引用进行强转:

struct EMAP_MSG_S
{
unsigned short msg;
unsigned short id;
char buf[100];
};


EMAP_MSG_S g_msg={
    0xFF,           //2字节
    0x1575,         //2字节
    "hello test"
};

//由于&优先级高,所以说明是个引用,其次表示是个指针类型,所以pRecvMessage是个指向指针的引用
void func(EMAP_MSG_S *&pRecvMessage)
{
    cout<<"buf:"<<pRecvMessage->buf<<endl;

    char *pBuf = reinterpret_cast<char *>(pRecvMessage) + 4;
    //偏移4字节,所以指向buf,等价于 VOS_CHAR *pBuf = (char *)(pRecvMessage) + 4;

    cout<<"pBuf:"<<pBuf<<endl;
}

int main()
{
    EMAP_MSG_S *pRecvMessage = &g_msg;

    func(pRecvMessage); //所以这里参数需要填入指针.
    return 0;

}

打印:

 

 

 

 

 

 

    

 

 

------------恢复内容结束------------

posted @ 2018-02-20 23:24  诺谦  阅读(1678)  评论(0编辑  收藏  举报