七、類型轉(zhuǎn)換

(1)C語言中的轉(zhuǎn)換

  • 隱式轉(zhuǎn)換:由范圍小的類型轉(zhuǎn)換成范圍大的類型
  • 格式:TypeName b = (TypeName)a;
    int n=10;
    float f=n;
    cout << f <<endl;//10
    float ff=10.2;
    n=ff;//截取
    cout << n <<endl;//10
    int* p=&n;
    void* v=p;//隱式轉(zhuǎn)換
    p=(int*)v;//顯示轉(zhuǎn)換

(2)C++11提供了四種轉(zhuǎn)換方式

  • 語法:xxx_cast<要轉(zhuǎn)換的類型>(被轉(zhuǎn)換的變量)
  • 作用:(1)細化準換類型;(2)提供安全性的檢測;(3)在代碼中更加醒目

(3)static_cast

<1>說明
  • 用于非多態(tài)類型之間的轉(zhuǎn)換,不提供運行時的檢查來確保轉(zhuǎn)換的安全性。
  • c++ 的任何的隱式轉(zhuǎn)換都是使用 static_cast 來實現(xiàn)。
<2>基本類型的轉(zhuǎn)換
    char c='a';
    cout << c <<endl;//a
    cout <<(int)c <<endl;//97(C語言方式)
    cout << static_cast<int>(c)<<endl;//97(C++中的轉(zhuǎn)換)
<3>指針的轉(zhuǎn)換
    int* arr1=(int*)malloc(sizeof(int)*10);
    int* arr2=static_cast<int*>(malloc(10*sizeof(int)));
<4>枚舉
enum BOOL{FALSE,TRUE};
    //BOOL b=0;//不能從int轉(zhuǎn)換為BOOL
    //BOOL b=(1==2);//不能從bool轉(zhuǎn)換為BOOL
    BOOL bb=static_cast<BOOL>(0);
    BOOL b=static_cast<BOOL>(1==2);
<5>基類和子類之間指針和引用的轉(zhuǎn)換
  • 上行轉(zhuǎn)換
    上行轉(zhuǎn)換,把子類的指針或引用轉(zhuǎn)換成父類,這種轉(zhuǎn)換是安全的(通常使用默認轉(zhuǎn)換)。
class Base{};
class Sub: public Base{};
//  上行 Sub -> Base
//編譯通過,安全
Sub sub;
Base *base_ptr = static_cast<Base*>(new Sub); 

通常使用隱式轉(zhuǎn)換

Base* pb=new Sub;
  • 下行轉(zhuǎn)換
    下行轉(zhuǎn)換,把父類的指針或引用轉(zhuǎn)換成子類,這種轉(zhuǎn)換是不安全的,也需要程序員來保證(通常使用dynamic_cast)。
//  下行 Base -> Sub
//編譯通過,不安全,需要使用dynamic_cast
Base base;
Sub *sub_ptr = static_cast<Sub*>(&base);  

(4)const_cast

<1>說明
  • 去掉const修飾的不可修改特性,只是在const_cast中去掉該特性,其他時候還是const變量
<2>基礎(chǔ)類型變量
  • 在<>里面不能用基本類型,只能用指針和引用
  • 基礎(chǔ)類型const常量在使用時展開操作,類似在宏定義中展開(編譯時之間進行值的替換),所以值沒有變.
    const int a=10;
    //const_cast<int>(a)=11;//注意:在<>里面不能用基本類型,只能用指針和引用
    const_cast<int&>(a)=12;//已經(jīng)改變a所在內(nèi)存的值
基礎(chǔ)類型const常量在使用時展開操作,類似在宏定義中展開,所以a的值沒有變
    cout << &a << "," << a <<endl;//0xabcd,10 // 常量展開,看不到改變
    const int* pa=&a;
    cout << pa << "," << *pa <<endl;//0xabcd,12 
    *const_cast<int*>(pa)=11;
    cout << &a << "," << a <<endl;//0xabcd,10 
    cout << pa << "," << *pa <<endl;//0xabcd,11
<3>string類型的變量
    const string s("abcd");
    //s+="def";//報錯
    cout << s <<endl;//abcd
    const_cast<string&>(s) +="def";
    cout << s <<endl;//abcddef
<4>類類型的對象
class Simple{
    int n;
public:
    Simple(int n):n(n){}
    void Set(int n){this->n=n;}
    int Get()const{return n;}
};
    const Simple s1(100);
    cout << s1.Get() << endl;//100
    const_cast<Simple&>(s1).Set(110);//const對象只能訪問const成員函數(shù),去掉s1的const屬性,<>里要用引用
    cout << s1.Get() << endl;//110
    const Simple* ps1=&s1;
    cout << ps1->Get() << endl;//110
    const_cast<Simple*>(ps1)->Set(120);
    cout << ps1->Get() <<endl;//120
<5>const成員函數(shù)中修改成員變量
  • const函數(shù)中所有成員變量都有const屬性。
  • const成員函數(shù)的本質(zhì):const函數(shù)中this指針是const類型,所以說所有成員不能修改。
  • const成員函數(shù)中修改成員變量有3中方法:
    (1)把需要修改的成員變量const_cast<>()轉(zhuǎn)成非const類型
    (2)把this使用const_cast<>()轉(zhuǎn)換成非const類型,然后修改成員變量
    (3)mutable的成員變量可以在const函數(shù)中修改
提供一個打印出Set/Get次數(shù)的函數(shù)
class Integer{
    int n;
    int setter;
    mutable int getter;//法三
public:
    Integer(int n):n(n),setter(0),getter(0){}
    void Set(int n){//非const成員函數(shù)中可以直接修改成員變量的值
        ++setter;
        this->n=n;
    }
    int Get()const{//const成員函數(shù)不能直接修改成員變量的值
        //++const_cast<int&>(getter);//法一
        //++(const_cast<Integer*>(this)->getter);//法二
        ++getter;
        return n;
    }

(5)dynamic_cast

用于類的指針、類的引用或者void *轉(zhuǎn)化,dynamic_cast只在多態(tài)有效。

<1>說明

主要用于以下種情況:

  • 上行轉(zhuǎn)換、下行轉(zhuǎn)換、交叉轉(zhuǎn)換
<2>上行轉(zhuǎn)換
  • 上行轉(zhuǎn)換:把子類的指針或引用轉(zhuǎn)換成父類,與static_cast相同。
<3>下行轉(zhuǎn)換
  • 下行轉(zhuǎn)換:把父類的指針或引用轉(zhuǎn)換成子類,dynamic_cast具有類型檢查的功能,比static_cast安全。使用時的2個條件:
    (1)父類指針必須指向當前類型的子類對象;
    (2)至少有一個虛函數(shù)
class Animal{
    string name;
public:
    Animal(const string& name):name(name){}
    virtual string Feature()const=0;//虛函數(shù)或者純虛函數(shù)都行
    //virtual string Feature()const{return " ";}
    const string& GetName()const{return name;}  
};
class Cat:public Animal{
    int n;
public:
    Cat(const string& name,int n):Animal(name),n(n){}//注意:對于非默認構(gòu)造函數(shù)的編寫
    string Feature()const{return "抓老鼠";}
    int GetMouseNum()const{return n;}
};

class Dog:public Animal{
public:
    Dog(const string& name):Animal(name){}
    string Feature()const{return "看門";}
};
void Display(Animal** arr,int n){//或者Animal** arr
    for(int i=0;i<n;++i){
        cout <<arr[i]->GetName()<<arr[i]->Feature();
// 下行轉(zhuǎn)換,把父類指針轉(zhuǎn)換成子類指針。1.父類指針必須指向當前類型的子類對象。2.必須有虛函數(shù)。
        Cat* cat=dynamic_cast<Cat*>(arr[i]);//cat有兩中結(jié)果:成功的話就是Cat*
        if(NULL!=cat){//失敗的的話就是NULL
            cout << "抓了"<<cat->GetMouseNum() << "只老鼠";
        }
        cout << endl;
    }
}
int main(){
    Animal* arr[]={new Cat("貓",5),new Dog("狗")};
    Display(arr,2);
}

如果時指針,進行正確的轉(zhuǎn)換,獲得對應(yīng)的值;否則返回NULL,如果是引用,則在運行時就會拋出異常;

  • 向下轉(zhuǎn)換的成功與否還與將要轉(zhuǎn)換的類型有關(guān),即要轉(zhuǎn)換的指針指向的對象的實際類型與轉(zhuǎn)換以后的對象類型一定要相同,否則轉(zhuǎn)換失敗。
<4>交叉轉(zhuǎn)換

交叉轉(zhuǎn)換,兄弟之間指針轉(zhuǎn)換

class Base {
public:
  void Print() { cout << "Base" << endl; }
  virtual ~Base(){}
};
class Derive1 : public Base {
public:
  void Print() { cout << "Derive1" << endl; }
};
class Derive2 : public Base {
public:
  void Print() { cout << "Derive2" << endl; }
};

int main() { 
    Derive1* pD1 = new Derive1;
    pD1->Print();//Derive1
    Derive2 *pD2 = dynamic_cast<Derive2*>(pD1);
    pD2->Print();//Derive2
}

dynamic_cast為什么只在多態(tài)的繼承關(guān)系才有效?由于運行時類型檢查需要運行時類型信息,而這個信息存儲在類的虛函數(shù)表。

(6)reinterpret_cast

  • 改變指針或引用的類型、
  • 對象指針的轉(zhuǎn)化(A類對象指針轉(zhuǎn)化成B類的對象指針,使用B類的成員函數(shù))
<1>將整型轉(zhuǎn)換為指針或引用類型。
    int n=100;
    //void* p=static_cast<void*>(n);
    void* p=reinterpret_cast<void*>(n);
    cout << p << endl;
<2>將指針或引用轉(zhuǎn)換為一個足夠長度的整型
    // 精度缺失不能轉(zhuǎn)換
    // n = reinterpret_cast<int>(p);
    // n = (int)p;
    long addr = reinterpret_cast<long>(p);
    cout << addr << endl;
<3>函數(shù)指針與指針的轉(zhuǎn)換
void Func(){
    cout << __func__ <<endl;
    cout << __cplusplus <<endl;//輸出編譯器版本
}
     //void* pf=static_cast<void*>(Func);
    void* pf=reinterpret_cast<void*>(Func);
    typedef void (*pfunc)();
    void (*func)()=reinterpret_cast<void(*)()>(pf);
    void (*func)()=reinterpret_cast<pfunc>(pf);//和上面一樣
<4>打印地址
  • 打印基本類型變量和數(shù)組的地址
    int n=10;
    int* p=&n;
    cout << p <<endl;
    
    int arr[]={1,2,4};
    cout << arr <<endl;
  • 打印字符串數(shù)組地址
    需要用static_cast
    const char* s="hello";
    cout << s <<" "<<static_cast<const void*>(s)<<endl;//直接打印會打印出字符串的值
    printf("%s\n",s);
    printf("%p\n",s);
  • 打印函數(shù)地址
    需要用reinterprete_cast
    void Func(){cout << __cplusplus <<endl;}//c++編譯器的版本
    //cout << Func <<endl;//直接打印函數(shù)名為1
    cout << (void*)Func <<endl;//C語言方式
    cout << reinterpret_cast<void*>(&Func) <<endl;
    printf("%p\n",Func);
<5>類中的偏移量和地址
  • 【&類名::成員變量】: 獲取成員變量在對象中的偏移量
  • 【&對象.成員變量】: 獲取成員變量的地址
class Point3D{
public:
    int x,y,z;
    Point3D(int x,int y,int z):x(x),y(y),z(z){}
    void Print()const{
        cout << "(" <<x << "," << y <<","<<z<<")"<<endl;
    }
};
int main(){
    Point3D point(1,2,3);
    point.Print();
1.成員變量在對象中的偏移量
printf("%d %d %d\n",&Point3D::x,&Point3D::y,&Point3D::z);//必須加&,因為不是static成員變量,沒有這種訪問方式
結(jié)果:0 4 8    
cout << &Point3D::x <<' '<<&Point3D::y <<' '<<&Point3D::z <<endl;
結(jié)果:1 1 1(沒啥用)
所以:要想打印對象中成員變量的偏移量需要用printf
2.對象的地址
    printf("%p\n",&point);
    cout << reinterpret_cast<void*>(&point)<<endl;
輸出:0061FF04
3.成員變量的地址
    printf("%p %p %p\n",&point.x,&point.y,&point.z);
    cout << reinterpret_cast<void*>(&point.x)<<endl;
輸出:0061FF04 0061FF08 0061FF0C
所以:成員變量的地址等于對象地址加上成員變量的偏移量
4.成員函數(shù)的地址
    cout <<(void*)&Point3D::Print<<endl;
    cout << reinterpret_cast<void*>(&Point3D::Print)<<endl;//獲取成員函數(shù)地址必須加&
    cout << reinterpret_cast<void*>(&point.Print)<<endl;

(7)總結(jié)

No. 轉(zhuǎn)換 轉(zhuǎn)換對象 作用 轉(zhuǎn)換時機
1 static_cast 基本類型、指針、引用 實現(xiàn)傳統(tǒng)的小括號轉(zhuǎn)化功能 在編譯期間實現(xiàn)轉(zhuǎn)換
2 const_cast const 類型的對象、指針、引用 移除變量const限定 在編譯期間實現(xiàn)轉(zhuǎn)換
3 dynamic_cast 類的指針、類的引用或者void * 多態(tài)父類指針/引用轉(zhuǎn)化成子類指針/引用 在運行期間實現(xiàn)轉(zhuǎn)換,并可以返回轉(zhuǎn)換成功與否的標志/拋出異常,有類型檢查
4 reinterpret_cast 指針、引用、算術(shù)類型 萬能強制類型轉(zhuǎn)換 在編譯期間實現(xiàn)轉(zhuǎn)換
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容