C++中的類型轉換運算符
零、小序
C++程序員都知道,C語言中的強制轉換和萬能轉換指針“void*”很常用,然而這樣的轉換有時候很不安全,甚至有時候是無意義的轉換!C風格的強制轉換和萬能轉換都需要程序員自己把握轉換的安全性,出現錯誤的可能性很大。面對C風格的鬆散轉換,C++增加了4個類型轉換運算符,用於規範類型轉換,程序員可以根據需要選擇一個類型轉換運算符,並讓編譯器去檢查類型轉換的安全性,提高代碼的安全性和健壯性!
一、dynamic_cast
1、關於dynamic_cast
dynamic_cast運算符是C++程序員經常使用的轉換類型,它也是最常用的RTTI(運行階段類型識別)組件,是基於類的繼承層次之間一種動態的轉換,這就要求基類必須有虛函數才能和子類之間進行轉換。
它既允許向上轉換(子類轉換爲父類)、也允許向下轉換(父類轉換爲子類)。只有指針類型與轉換的對象類型(或者是對象的直接或間接的基類)相同時才一定是安全的。因此向上轉換是安全的,而向下轉換不一定安全。轉換不成功時,指針會返回空值。
dynamic_cast也可以用於引用的轉換,但是由於沒有和空指針對應的引用值,當引用轉換不正確時,會拋出異常。
2、代碼示例
// DynamicCastType.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
using namespace std;
class Grand
{
public:
Grand() {}
~Grand() {}
virtual void printName()
{
cout << "I'm Grand!" << endl;
}
private:
};
class Son : public Grand
{
public:
Son() {}
~Son() {}
virtual void printName()
{
cout << "I'm Son!" << endl;
}
void sayName()
{
cout << "I'm Jack!" << endl;
}
private:
};
class GrandSon : public Son
{
public:
GrandSon() {}
~GrandSon() {}
virtual void printName()
{
cout << "I'm GrandSon!" << endl;
}
void sayAge()
{
cout << "My age is 18!" << endl;
}
private:
};
#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}
int main()
{
cout << "-----------------dynamic_cast類型轉換------------------" << endl;
Grand *pGrand = new Grand;
Son *pSon = new Son;
GrandSon *pGrandSon = new GrandSon;
cout << "-----------------向上轉換安全------------------" << endl;
Grand *pTmpGrand1 = dynamic_cast<Grand*>(pSon); // 向上轉換安全
pTmpGrand1->printName();
Grand *pTmpGrand2 = dynamic_cast<Grand*>(pGrandSon); // 向上轉換安全
pTmpGrand2->printName();
Son *pTmpGrandSon1 = dynamic_cast<GrandSon*>(pGrandSon); // 向上轉換安全
pTmpGrandSon1->printName();
cout << "-----------------向下轉換不一定安全------------------" << endl;
Son *pSon1 = dynamic_cast<Son*>(pGrand);
if (pSon1 != nullptr)
{
pSon1->sayName();
}
else
{
cout << "指針pSon1是空的!" << endl;
}
GrandSon *pGrandSon1 = dynamic_cast<GrandSon*>(pGrand);
if (pGrandSon1 != nullptr)
{
pGrandSon1->sayAge();
}
else
{
cout << "指針pGrandSon1是空的!" << endl;
}
// 只需要釋放這三個指針就行,一塊內存只允許釋放一次,其他指針都是由這三個指針轉換的,地址都是同一塊
DELETE_PTR(pGrand);
DELETE_PTR(pSon);
DELETE_PTR(pGrandSon);
std::cout << "Hello World!\n";
getchar();
}
運行結果:
二、static_cast
1、關於static_cast
static_cast運算符也是C++程序員經常使用的轉換類型,與dynamic_cast相比,它是一種靜態的轉換,使用它在基類和子類之間的互相轉換是合法的,但合法並不代表安全!
它像dynamic_cast一樣既允許向上轉換(子類轉換爲父類)、也允許向下轉換(父類轉換爲子類)。只要相互轉換的類型有關係(直接或者間接繼承)轉換都是合法的,切記合法不一定安全!它的使用接近C風格的強制類型轉換,安全性需要程序員自己把握。
由於static_cast是無需進行類型轉換的,所以它可以將枚舉值轉換成整型,也可以將整型轉換爲枚舉值;同樣可以將doube、int、float、long等各種數值之間進行轉換,但要注意轉換精度,確保是自己需要的轉換。
2、代碼示例
// StaticCastType.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
using namespace std;
class Grand
{
public:
Grand() {}
~Grand() {}
virtual void printName()
{
cout << "I'm Grand!" << endl;
}
private:
};
class Son : public Grand
{
public:
Son() {}
~Son() {}
virtual void printName()
{
cout << "I'm Son!" << endl;
}
void sayName()
{
cout << "I'm Jack!" << endl;
}
private:
};
class GrandSon : public Son
{
public:
GrandSon() {}
~GrandSon() {}
virtual void printName()
{
cout << "I'm GrandSon!" << endl;
}
void sayAge()
{
cout << "My age is 18!" << endl;
}
private:
};
#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}
int main()
{
cout << "-----------------static_cast類型轉換------------------" << endl;
cout << "-----------------static_cast類型用於指針轉換------------------" << endl;
Grand *pGrand = new Grand;
Son *pSon = new Son;
GrandSon *pGrandSon = new GrandSon;
cout << "-----------------向上轉換安全------------------" << endl;
Grand *pTmpGrand1 = static_cast<Grand*>(pSon); // 向上轉換安全
pTmpGrand1->printName();
Grand *pTmpGrand2 = static_cast<Grand*>(pGrandSon); // 向上轉換安全
pTmpGrand2->printName();
Son *pTmpGrandSon1 = static_cast<GrandSon*>(pGrandSon); // 向上轉換安全
pTmpGrandSon1->printName();
cout << "-----------------向下轉換不一定安全------------------" << endl;
Son *pSon1 = static_cast<Son*>(pGrand);
if (pSon1 != nullptr)
{
pSon1->sayName();
}
else
{
cout << "指針pSon1是空的!" << endl;
}
GrandSon *pGrandSon1 = static_cast<GrandSon*>(pGrand);
if (pGrandSon1 != nullptr)
{
pGrandSon1->sayAge();
}
else
{
cout << "指針pGrandSon1是空的!" << endl;
}
cout << "-----------------static_cast類型用於數值轉換------------------" << endl;
int iNum = 100;
float fNum = 150.5;
short sNum = static_cast<short>(iNum);
int iTmpNum = static_cast<int>(fNum);
double dNum = static_cast<double>(iNum);
cout << "iNum=" << iNum << endl;
cout << "fNum=" << fNum << endl;
cout << "sNum=" << sNum << endl;
cout << "iTmpNum=" << iTmpNum << endl;
cout << "dNum=" << dNum << endl;
// 只需要釋放這三個指針就行,一塊內存只允許釋放一次,其他指針都是由這三個指針轉換的,地址都是同一塊
DELETE_PTR(pGrand);
DELETE_PTR(pSon);
DELETE_PTR(pGrandSon);
std::cout << "Hello World!\n";
getchar();
}
運行結果:
三、const_cast
1、關於const_cast
const_cast運算符的只有一個作用就是去const化,將一個常量類型轉換爲一個可以修改其他值的非常量類型。
這個運算符使用的場景是,需要一個值大多數時候都是不可變的,把這個值聲明爲const類型,而在某種時刻又是需要可以修改的,此時可以使用const_cast進行修改它。
2、代碼示例
// ConstCastType.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
using namespace std;
class Grand
{
public:
Grand() {}
~Grand() {}
virtual void printName()
{
cout << "I'm Grand!" << endl;
}
private:
};
class Son : public Grand
{
public:
Son() {}
~Son() {}
virtual void printName()
{
cout << "I'm Son!" << endl;
}
void sayName()
{
cout << "I'm Jack!" << endl;
}
private:
};
#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}
int main()
{
cout << "-----------------const_cast類型轉換------------------" << endl;
const char * cStr = "ISmileLI";
char* cTmpStr = const_cast<char*>(cStr); // cStr本身是個常量沒有變,它的返回值已經是一個非常量
cout << "cStr:" << cStr << endl;
cout << "cTmpStr:" << cTmpStr << endl;
cout << "-----------------const_cast允許去掉一個類對象的const------------------" << endl;
const Grand *pGrand = new Grand;
Grand *pTmpGrand = const_cast<Grand *>(pGrand);
if (pTmpGrand != nullptr)
{
pTmpGrand->printName();
}
else
{
cout << "指針pTmpGrand是空的!" << endl;
}
cout << "-----------------const_cast不允許轉換類對象的類型------------------" << endl;
const Son *pSon = new Son;
//Grand *pTmpGrand2 = const_cast<Grand *>(pSon); // 編譯器會提示錯誤
cout << "//Grand *pTmpGrand2 = const_cast<Grand *>(pSon); // 編譯器會提示錯誤" << endl;
// const類型的指針的內存空間不需要手動釋放,所以不需要調用下面的代碼
//DELETE_PTR(cStr);
//DELETE_PTR(pGrand);
std::cout << "Hello World!\n";
getchar();
}
運行結果:
四、reinterpret_cast
1、關於reinterpret_cast
reinterpret_cast運算符是這四種轉換運算符中最不安全的、風險最大的轉換類型,它主要適用於依賴底層實現的編程技術,因爲不繫統一個類型存儲的字節可能是不一樣的,所以具有不可移植性、對平臺的依賴性強。
存在即合理!它可以對無關類指針進行轉換,甚至可以直接將整型值轉成指針,也可以將指針轉換成足以存儲指針的整型。
2、代碼示例
// ReinterpretCastType.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//
#include <iostream>
using namespace std;
class Data
{
public:
short a;
short b;
};
#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}
int main()
{
cout << "-----------------reinterpret_cast類型轉換------------------" << endl;
long value = 0xA222B111;
Data *pData = reinterpret_cast<Data*>(&value);
cout << "pData->a的十六進制輸出:"<< hex << pData->a << endl;
cout << "pData->b的十六進制輸出:" << hex << pData->b << endl;
// 不需要釋放指針pData的空間,它的地址和變量value相同,會自動釋放掉
// DELETE_PTR(pData);
std::cout << "Hello World!\n";
getchar();
}
運行結果:
五、總述
dynamic_cast:主要用於動態類型轉換,可以進行向上和向下轉換,向上轉換是安全的,向下轉換不一定安全,轉換失敗會返回空指針或者拋出異常。
static_cast:主要用於靜態類型轉換,可以進行向上和向下轉換,向上或者向下轉換都是合法的,但合法並不代表安全!安全性需要程序員自己把握,類似於C風格的強制轉換。
const_cast:只有一個作用就是去const化,將一個常量類型轉換爲一個可以修改其他值的非常量類型。
reinterpret_cast:最不安全的、風險最大的轉換類型,它主要適用於依賴底層實現的編程技術,因爲不繫統一個類型存儲的字節可能是不一樣的,所以具有不可移植性、對平臺的依賴性強。它可以對無關類指針進行轉換,甚至可以直接將整型值轉成指針,也可以將指針轉換成足以存儲指針的整型。
原創不易,點贊鼓勵一下吧!