程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++中static_cast/const_cast/dynamic_cast/reinterpret_cast的區別和使用

C++中static_cast/const_cast/dynamic_cast/reinterpret_cast的區別和使用

編輯:關於C++

C風格的強制轉換較簡單,如將float a轉換為int b,則可以這樣:b = (int)a,或者b=int(a)。

C++類型轉換分為隱式類型轉換和顯示類型轉換。

隱式類型轉換又稱為標准轉換,包括以下幾種情況:

(1)、算術轉換:在混合類型的算術表達式中,最寬的數據類型成為目標轉換類型;

(2)、一種類型表達式賦值給另一種類型的對象:目標類型是被賦值對象的類型;

(3)、將一個表達式作為實參傳遞給函數調用,此時形參和實參類型不一致:目標轉換類型為形參的類型;

(4)、從一個函數返回一個表達式,表達式類型與返回類型不一致:目標轉換類型為函數的返回類型。

顯示類型轉換被稱為強制類型轉換(cast)。

C++提供了四種強制類型轉換形式:static_cast、const_cast、dynamic_cast、reinterpret_cast.每一種適用於特定的場合。

static_cast語法:static_cast(expression)

static_cast:僅根據表達式中存在的類型,將expression轉換為type-id類型。此運算符可用於將指向基類的指針轉換為指向派生類的指針等操作。此類轉換並非始終安全。

通常使用 static_cast 轉換數值數據類型,例如將枚舉型轉換為整型或將整型轉換為浮點型,而且你能確定參與轉換的數據類型。 static_cast轉換安全性不如dynamic_cast轉換,因static_cast不執行運行時類型檢查,而dynamic_cas執行該檢查。對不明確的指針的 dynamic_cast將失敗,而static_cast的返回結果看似沒有問題,這是危險的。盡管 dynamic_cast轉換更加安全,但是dynamic_cast只適用於指針或引用,而且運行時類型檢查也是一項開銷。dynamic_cast 和static_cast運算符可以在整個類層次結構中移動指針。然而,static_cast完全依賴於轉換語句提供的信息,因此可能不安全。

static_cast可以反向執行隱式轉換,可用於任何隱式允許的轉換類型,而在這種情況下結果是不確定的。這需要程序員來驗證static_cast轉換的結果是否安全。

static_cast可用於將int轉換為char。但是,得到的char可能沒有足夠的位來保存整個int值。同樣,這需要程序員來驗證static_cast轉換的結果是否安全。

static_cast運算符還可用於執行任何隱式轉換,包括標准轉換和用戶定義的轉換。

static_cast 運算符可以將整數值顯式轉換為枚舉類型。如果整型值不在枚舉值的范圍內,生成的枚舉值是不確定的。

static_cast運算符將null指針值轉換為目標類型的null指針值。

任何表達式都可以通過static_cast運算符顯式轉換為void類型。目標void類型可以選擇性地包含const、volatile或__unaligned特性。

static_cast運算符無法轉換掉const、volatile或 __unaligned特性。

只有在確信代碼將正常運行的時候,在性能關鍵代碼中使用 static_cast。如果必須在發布模式下使用static_cast,請在調試版本中用 safe_cast(C++ 組件擴展)替換它以確保成功。

const_cast語法:const_cast(expression)

const_cast:從類中移除const、volatile和__unaligned特性。

指向任何對象類型的指針或指向數據成員的指針可顯式轉換為完全相同的類型(const、volatile 和 __unaligned 限定符除外)。對於指針和引用,結果將引用原始對象。對於指向數據成員的指針,結果將引用與指向數據成員的原始(未強制轉換)的指針相同的成員。根據引用對象的類型,通過生成的指針、引用或指向數據成員的指針的寫入操作可能產生未定義的行為。

不能使用const_cast運算符直接重寫常量變量的常量狀態。

const_cast運算符將null指針值轉換為目標類型的null指針值。

dynamic_cast語法:dynamic_cast(expression)

dynamic_cast:type-id必須是一個指針或引用到以前已定義的類類型的引用或“指向 void的指針”。如果type-id是指針,則expression的類型必須是指針,如果type-id是引用,則為左值。

在托管代碼中的 dynamic_cast的行為中有兩個重大更改:(1)、為指針的dynamic_cast對指向裝箱的枚舉的基礎類型的指針將在運行時失敗,則返回0而不是已轉換的指針。(2)、dynamic_cast 將不再引發一個異常,當type-id是指向值類型的內部指針,則轉換在運行時失敗。該轉換將返回0指示運行值而不是引發。

如果type-id是指向expression的明確的可訪問的直接或間接基類的指針,則結果是指向type-id類型的唯一子對象的指針。

如果type-id為void*,則做運行時進行檢查確定expression的實際類型。結果是指向byexpression的完整的對象的指針。

如果type-id不是 void*,則做運行時進行檢查以確定是否由expression指向的對象可以轉換為由type-id指向的類型。如果expression類型是type-id類型的基類,則做運行時檢查來看是否expression確實指向type-id類型的完整對象。如果為true,則結果是指向type-id類型的完整對象的指針。

dynamic_cast運算符還可以使用執行“相互轉換”。使用同一個類層次結構可能進行指針轉換。

當使用dynamic_cast時,如果expression無法安全地轉換成類型type-id,則運行時檢查會引起變換失敗。

指針類型的非限定轉換的值是null指針。引用類型的非限定轉換會引發bad_cast異常。

reinterpret_cast語法:reinterpret_cast(expression)

reinterpret_cast:允許將任何指針轉換為任何其他指針類型。也允許將任何整數類型轉換為任何指針類型以及反向轉換。

濫用reinterpret_cast運算符可能很容易帶來風險。除非所需轉換本身是低級別的,否則應使用其他強制轉換運算符之一。

reinterpret_cast運算符可用於char*到int*或One_class*到Unrelated_class*之類的轉換,這本身並不安全。

reinterpret_cast的結果不能安全地用於除強制轉換回其原始類型以外的任何用途。在最好的情況下,其他用途也是不可移植的。

reinterpret_cast運算符不能丟掉const、volatile或__unaligned特性。

reinterpret_cast運算符將null指針值轉換為目標類型的null指針值。

reinterpret_cast的一個實際用途是在哈希函數中,即,通過讓兩個不同的值幾乎不以相同的索引結尾的方式將值映射到索引。

static_cast is the first cast you should attempt to use. It does things like implicit conversions between types (such as int to float, or pointer to void*), and it can also call explicit conversion functions (or implicit ones). In many cases,explicitly stating static_cast isn't necessary, but it's important to note that the T(something) syntax is equivalent to (T)something and should be avoided(more on that later). A T(something, something_else) is safe, however, and guaranteed to call the constructor.

static_cast can also cast through inheritance hierarchies. It is unnecessary when casting upwards (towards a base class), but when casting downwards it can be used as long as it doesn't cast through virtual inheritance. It does not do checking,however, and it is undefined behavior to static_cast down a hierarchy to a type that isn't actually the type of the object.

const_cast can be used to remove or add const to a variable; no other C++ cast is capable of removing it (not even reinterpret_cast). It is important to note that modifying a formerly const value is only undefined if the original variable is const; if you use it to take the const off a reference to something that wasn't declared with const, it is safe. This can be useful when overloading member functions based on const, for instance. It can also be used to add const to an object,such as to call a member function overload.

const_cast also works similarly on volatile, though that's less common.

dynamic_cast is almost exclusively used for handling polymorphism. You can cast a pointer or reference to any polymorphic type to any other class type (a polymorphic type has at least one virtual function, declared or inherited). You can use it for more than just casting downwards -- you can cast sideways or even up another chain. The dynamic_cast will seek out the desired object and return it if possible. If it can't, it will return nullptr in the case of a pointer, or throw std::bad_cast in the case of a reference.

dynamic_cast has some limitations, though. It doesn't work if there are multiple objects of the same type in the inheritance hierarchy (the so-called 'dreaded diamond') and you aren't using virtual inheritance. It also can only go through public inheritance - it will always fail to travel through protected or private inheritance. This is rarely an issue, however, as such forms of inheritance are rare.

reinterpret_cast is the most dangerous cast, and should be used very sparingly. It turns one type directly into another - such as casting the value from one pointer to another, or storing a pointer in an int, or all sorts of other nasty things.Largely, the only guarantee you get with reinterpret_cast is that normally if you cast the result back to the original type, you will get the exact same value (but not if the intermediate type is smaller than the original type).There are a number of conversions that reinterpret_cast cannot do, too. It's used primarily for particularly weird conversions and bit manipulations, like turning a raw data stream into actual data, or storing data in the low bits of an aligned pointer.

C casts are casts using (type)object or type(object). A C-style cast is defined as the first of the following which succeeds:(1)、const_cast; (2)、static_cast(though ignoring access restrictions); (3)、static_cast (see above), then const_cast; (4)、reinterpret_cast; (5)、reinterpret_cast, then const_cast。

It can therefore be used as a replacement for other casts in some instances, but can be extremely dangerous because of the ability to devolve into a reinterpret_cast,and the latter should be preferred when explicit casting is needed, unless you are sure static_cast will succeed or reinterpret_cast will fail. Even then,consider the longer, more explicit option.

C-style casts also ignore access control when performing a static_cast, which means that they have the ability to perform an operation that no other cast can. This is mostly a kludge, though, and in my mind is just another reason to avoid C-style casts.

測試代碼如下:

static_cast.hpp:

 

#ifndef FBC_MESSY_TEST_STATIC_CAST_HPP_
#define FBC_MESSY_TEST_STATIC_CAST_HPP_

#include 

// reference: https://msdn.microsoft.com/zh-cn/library/c36yw7x9.aspx
class B1 {
public:
	virtual void Test(){}
};

class D1 : public B1 {};

class CCTest {
public:
	void setNumber(int);
	void printNumber() const;
private:
	int number;
};

class B2 { };
class C2 : public B2 { };
class D2 : public C2 { };

class A3 { virtual void f(); };
class B3 { virtual void f(); };

class B4 { virtual void f(); };
class D4 : public B4 { virtual void f(); };

// Returns a hash code based on an address
unsigned short Hash(void *p);

void test_static_cast1();
void test_static_cast2(B1* pb, D1* pd);
void test_static_cast3(B1* pb);
void test_static_cast4();
void test_static_cast5();
void test_static_cast6();
void test_static_cast7();
void test_static_cast8();
void test_static_cast9();
void test_static_cast10();

#endif // FBC_MESSY_TEST_STATIC_CAST_HPP_
static_cast.cpp:

 

 

#include "static_cast.hpp"
#include 

void CCTest::setNumber(int num) { number = num; }

void CCTest::printNumber() const {
	std::cout << "\nBefore: " << number;
	//this 指針的數據類型為 const CCTest *。
	//const_cast 運算符會將 this 指針的數據類型更改為 CCTest *,以允許修改成員 number。
	//強制轉換僅對其所在的語句中的其余部分持續
	const_cast< CCTest * >(this)->number--;
	std::cout << "\nAfter: " << number;
}

void A3::f()
{

}

void B3::f()
{

}

void B4::f()
{

}

void D4::f()
{

}

unsigned short Hash(void *p) {
	//reinterpret_cast 允許將指針視為整數類型。結果隨後將按位移位並與自身進行“異或”運算以生成唯一的索引(具有唯一性的概率非常高)。
	//該索引隨後被標准 C 樣式強制轉換截斷為函數的返回類型。
	unsigned int val = reinterpret_cast(p);
	return (unsigned short)(val ^ (val >> 16));
}

// C風格強制類型轉換
void test_static_cast1()
{
	float a = 1.1, b = 1.9;

	int ret1 = (int)a;
	int ret2 = int(b);

	std::cout << ret1 << "    " << ret2 << "    " << std::endl;
}

void test_static_cast2(B1* pb, D1* pd)
{
	//與 dynamic_cast 不同,pb 的 static_cast 轉換不執行運行時檢查。
	//由 pb 指向的對象可能不是 D 類型的對象,在這種情況下使用 *pd2 會是災難性的。
	//例如,調用 D 類(而非 B 類)的成員函數可能會導致訪問沖突。
	D1* pd2 = static_cast(pb);   // Not safe, D can have fields and methods that are not in B.
	B1* pb2 = static_cast(pd);   // Safe conversion, D always contains all of B.
}

void test_static_cast3(B1* pb)
{
	//如果 pb 確實指向 D 類型的對象,則 pd1 和 pd2 將獲取相同的值。如果 pb == 0,它們也將獲取相同的值。
	//如果 pb 指向 B 類型的對象,而非指向完整的 D 類,則 dynamic_cast 足以判斷返回零。
	//但是,static_cast 依賴於程序員的斷言,即 pb 指向 D 類型的對象,因而只是返回指向那個假定的 D 對象的指針。
	D1* pd1 = dynamic_cast(pb);
	D1* pd2 = static_cast(pb);
}

void test_static_cast4()
{
	char ch;
	int i = 65;
	float f = 2.5;
	double dbl;

	ch = static_cast(i);   // int to char
	dbl = static_cast(f);   // float to double
	i = static_cast(ch);
}

void test_static_cast5()
{
	CCTest X;
	X.setNumber(8);
	X.printNumber();
}

void test_static_cast6(D2* pd)
{
	//此轉換類型稱為“向上轉換”,因為它將在類層次結構上的指針,從派生的類移到該類派生的類。向上轉換是一種隱式轉換。
	C2* pc = dynamic_cast(pd);   // ok: C is a direct base class pc points to C subobject of pd 
	B2* pb = dynamic_cast(pd);   // ok: B is an indirect base class pb points to B subobject of pd
}

void test_static_cast7()
{
	A3* pa = new A3;
	B3* pb = new B3;
	void* pv = dynamic_cast(pa);// pv now points to an object of type A
	pv = dynamic_cast(pb);// pv now points to an object of type B
}

void test_static_cast8()
{
	B4* pb = new D4;   // unclear but ok
	B4* pb2 = new B4;

	//此轉換類型稱為“向下轉換”,因為它將在類層次結構下的指針,從給定的類移到該類派生的類。
	D4* pd = dynamic_cast(pb);   // ok: pb actually points to a D
	D4* pd2 = dynamic_cast(pb2);   // pb2 points to a B not a D
}

void test_static_cast9()
{
	A3* pa = new A3;
	B3* pb = dynamic_cast(pa);   // fails at runtime, not safe;B not derived from A
}

void test_static_cast10()
{
	int a[20];
	for (int i = 0; i < 20; i++) {
		std::cout << Hash(a + i) << std::endl;
	}
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved