C++中的類型轉換運算符dynamic_cast、static_cast、const_cast和reinterpret_cast詳解

零、小序

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:最不安全的、風險最大的轉換類型,它主要適用於依賴底層實現的編程技術,因爲不繫統一個類型存儲的字節可能是不一樣的,所以具有不可移植性、對平臺的依賴性強。它可以對無關類指針進行轉換,甚至可以直接將整型值轉成指針,也可以將指針轉換成足以存儲指針的整型。

原創不易,點贊鼓勵一下吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章