C++中static_cast/const_cast/dynamic_cast/reinterpret_cast的区别和使用
C風(fēng)格的強(qiáng)制轉(zhuǎn)換較簡(jiǎn)單,如將float a轉(zhuǎn)換為int b,則可以這樣:b = (int)a,或者b=int(a)。
C++類型轉(zhuǎn)換分為隱式類型轉(zhuǎn)換和顯示類型轉(zhuǎn)換。
隱式類型轉(zhuǎn)換又稱為標(biāo)準(zhǔn)轉(zhuǎn)換,包括以下幾種情況:
(1)、算術(shù)轉(zhuǎn)換:在混合類型的算術(shù)表達(dá)式中,最寬的數(shù)據(jù)類型成為目標(biāo)轉(zhuǎn)換類型;
(2)、一種類型表達(dá)式賦值給另一種類型的對(duì)象:目標(biāo)類型是被賦值對(duì)象的類型;
(3)、將一個(gè)表達(dá)式作為實(shí)參傳遞給函數(shù)調(diào)用,此時(shí)形參和實(shí)參類型不一致:目標(biāo)轉(zhuǎn)換類型為形參的類型;
(4)、從一個(gè)函數(shù)返回一個(gè)表達(dá)式,表達(dá)式類型與返回類型不一致:目標(biāo)轉(zhuǎn)換類型為函數(shù)的返回類型。
顯示類型轉(zhuǎn)換被稱為強(qiáng)制類型轉(zhuǎn)換(cast)。
C++提供了四種強(qiáng)制類型轉(zhuǎn)換形式:static_cast、const_cast、dynamic_cast、reinterpret_cast.每一種適用于特定的場(chǎng)合。
static_cast語(yǔ)法:static_cast<type-id>(expression)
static_cast:僅根據(jù)表達(dá)式中存在的類型,將expression轉(zhuǎn)換為type-id類型。此運(yùn)算符可用于將指向基類的指針轉(zhuǎn)換為指向派生類的指針等操作。此類轉(zhuǎn)換并非始終安全。
通常使用 static_cast 轉(zhuǎn)換數(shù)值數(shù)據(jù)類型,例如將枚舉型轉(zhuǎn)換為整型或?qū)⒄娃D(zhuǎn)換為浮點(diǎn)型,而且你能確定參與轉(zhuǎn)換的數(shù)據(jù)類型。 static_cast轉(zhuǎn)換安全性不如dynamic_cast轉(zhuǎn)換,因static_cast不執(zhí)行運(yùn)行時(shí)類型檢查,而dynamic_cas執(zhí)行該檢查。對(duì)不明確的指針的 dynamic_cast將失敗,而static_cast的返回結(jié)果看似沒(méi)有問(wèn)題,這是危險(xiǎn)的。盡管 dynamic_cast轉(zhuǎn)換更加安全,但是dynamic_cast只適用于指針或引用,而且運(yùn)行時(shí)類型檢查也是一項(xiàng)開銷。dynamic_cast 和static_cast運(yùn)算符可以在整個(gè)類層次結(jié)構(gòu)中移動(dòng)指針。然而,static_cast完全依賴于轉(zhuǎn)換語(yǔ)句提供的信息,因此可能不安全。
static_cast可以反向執(zhí)行隱式轉(zhuǎn)換,可用于任何隱式允許的轉(zhuǎn)換類型,而在這種情況下結(jié)果是不確定的。這需要程序員來(lái)驗(yàn)證static_cast轉(zhuǎn)換的結(jié)果是否安全。
static_cast可用于將int轉(zhuǎn)換為char。但是,得到的char可能沒(méi)有足夠的位來(lái)保存整個(gè)int值。同樣,這需要程序員來(lái)驗(yàn)證static_cast轉(zhuǎn)換的結(jié)果是否安全。
static_cast運(yùn)算符還可用于執(zhí)行任何隱式轉(zhuǎn)換,包括標(biāo)準(zhǔn)轉(zhuǎn)換和用戶定義的轉(zhuǎn)換。
static_cast 運(yùn)算符可以將整數(shù)值顯式轉(zhuǎn)換為枚舉類型。如果整型值不在枚舉值的范圍內(nèi),生成的枚舉值是不確定的。
static_cast運(yùn)算符將null指針值轉(zhuǎn)換為目標(biāo)類型的null指針值。
任何表達(dá)式都可以通過(guò)static_cast運(yùn)算符顯式轉(zhuǎn)換為void類型。目標(biāo)void類型可以選擇性地包含const、volatile或__unaligned特性。
static_cast運(yùn)算符無(wú)法轉(zhuǎn)換掉const、volatile或 __unaligned特性。
只有在確信代碼將正常運(yùn)行的時(shí)候,在性能關(guān)鍵代碼中使用 static_cast。如果必須在發(fā)布模式下使用static_cast,請(qǐng)?jiān)谡{(diào)試版本中用 safe_cast(C++ 組件擴(kuò)展)替換它以確保成功。
任何具有明確定義的類型轉(zhuǎn)換,只要不包含底層const,都可以使用static_cast。
const_cast語(yǔ)法:const_cast<type-id>(expression)
const_cast:從類中移除const、volatile和__unaligned特性。
指向任何對(duì)象類型的指針或指向數(shù)據(jù)成員的指針可顯式轉(zhuǎn)換為完全相同的類型(const、volatile 和 __unaligned 限定符除外)。對(duì)于指針和引用,結(jié)果將引用原始對(duì)象。對(duì)于指向數(shù)據(jù)成員的指針,結(jié)果將引用與指向數(shù)據(jù)成員的原始(未強(qiáng)制轉(zhuǎn)換)的指針相同的成員。根據(jù)引用對(duì)象的類型,通過(guò)生成的指針、引用或指向數(shù)據(jù)成員的指針的寫入操作可能產(chǎn)生未定義的行為。
不能使用const_cast運(yùn)算符直接重寫常量變量的常量狀態(tài)。
const_cast運(yùn)算符將null指針值轉(zhuǎn)換為目標(biāo)類型的null指針值。
dynamic_cast語(yǔ)法:dynamic_cast<type-id>(expression)
dynamic_cast:type-id必須是一個(gè)指針或引用到以前已定義的類類型的引用或“指向 void的指針”。如果type-id是指針,則expression的類型必須是指針,如果type-id是引用,則為左值。
在托管代碼中的 dynamic_cast的行為中有兩個(gè)重大更改:(1)、為指針的dynamic_cast對(duì)指向裝箱的枚舉的基礎(chǔ)類型的指針將在運(yùn)行時(shí)失敗,則返回0而不是已轉(zhuǎn)換的指針。(2)、dynamic_cast 將不再引發(fā)一個(gè)異常,當(dāng)type-id是指向值類型的內(nèi)部指針,則轉(zhuǎn)換在運(yùn)行時(shí)失敗。該轉(zhuǎn)換將返回0指示運(yùn)行值而不是引發(fā)。
如果type-id是指向expression的明確的可訪問(wèn)的直接或間接基類的指針,則結(jié)果是指向type-id類型的唯一子對(duì)象的指針。
如果type-id為void*,則做運(yùn)行時(shí)進(jìn)行檢查確定expression的實(shí)際類型。結(jié)果是指向byexpression的完整的對(duì)象的指針。
如果type-id不是 void*,則做運(yùn)行時(shí)進(jìn)行檢查以確定是否由expression指向的對(duì)象可以轉(zhuǎn)換為由type-id指向的類型。如果expression類型是type-id類型的基類,則做運(yùn)行時(shí)檢查來(lái)看是否expression確實(shí)指向type-id類型的完整對(duì)象。如果為true,則結(jié)果是指向type-id類型的完整對(duì)象的指針。
dynamic_cast運(yùn)算符還可以使用執(zhí)行“相互轉(zhuǎn)換”。使用同一個(gè)類層次結(jié)構(gòu)可能進(jìn)行指針轉(zhuǎn)換。
當(dāng)使用dynamic_cast時(shí),如果expression無(wú)法安全地轉(zhuǎn)換成類型type-id,則運(yùn)行時(shí)檢查會(huì)引起變換失敗。
指針類型的非限定轉(zhuǎn)換的值是null指針。引用類型的非限定轉(zhuǎn)換會(huì)引發(fā)bad_cast異常。
dynamic_cast支持運(yùn)行時(shí)類型識(shí)別。
reinterpret_cast語(yǔ)法:reinterpret_cast<type-id>(expression)
reinterpret_cast:允許將任何指針轉(zhuǎn)換為任何其他指針類型。也允許將任何整數(shù)類型轉(zhuǎn)換為任何指針類型以及反向轉(zhuǎn)換。
濫用reinterpret_cast運(yùn)算符可能很容易帶來(lái)風(fēng)險(xiǎn)。除非所需轉(zhuǎn)換本身是低級(jí)別的,否則應(yīng)使用其他強(qiáng)制轉(zhuǎn)換運(yùn)算符之一。
reinterpret_cast運(yùn)算符可用于char*到int*或One_class*到Unrelated_class*之類的轉(zhuǎn)換,這本身并不安全。
reinterpret_cast的結(jié)果不能安全地用于除強(qiáng)制轉(zhuǎn)換回其原始類型以外的任何用途。在最好的情況下,其他用途也是不可移植的。
reinterpret_cast運(yùn)算符不能丟掉const、volatile或__unaligned特性。
reinterpret_cast運(yùn)算符將null指針值轉(zhuǎn)換為目標(biāo)類型的null指針值。
reinterpret_cast的一個(gè)實(shí)際用途是在哈希函數(shù)中,即,通過(guò)讓兩個(gè)不同的值幾乎不以相同的索引結(jié)尾的方式將值映射到索引。
reinterpret_cast通常為運(yùn)算對(duì)象的位模式提供較低層次上的重新解釋。reinterpret_cast本質(zhì)上依賴機(jī)器。要想安全地使用reinterpret_cast必須對(duì)涉及的類型和編譯器實(shí)現(xiàn)轉(zhuǎn)換的過(guò)程都非常了解。
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.
測(cè)試代碼如下:
static_cast.hpp:
#ifndef FBC_MESSY_TEST_STATIC_CAST_HPP_
#define FBC_MESSY_TEST_STATIC_CAST_HPP_#include <iostream>// 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 <iostream>void CCTest::setNumber(int num) { number = num; }void CCTest::printNumber() const {std::cout << "\nBefore: " << number;//this 指針的數(shù)據(jù)類型為 const CCTest *。//const_cast 運(yùn)算符會(huì)將 this 指針的數(shù)據(jù)類型更改為 CCTest *,以允許修改成員 number。//強(qiáng)制轉(zhuǎn)換僅對(duì)其所在的語(yǔ)句中的其余部分持續(xù)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 允許將指針視為整數(shù)類型。結(jié)果隨后將按位移位并與自身進(jìn)行“異或”運(yùn)算以生成唯一的索引(具有唯一性的概率非常高)。//該索引隨后被標(biāo)準(zhǔn) C 樣式強(qiáng)制轉(zhuǎn)換截?cái)酁楹瘮?shù)的返回類型。unsigned int val = reinterpret_cast<unsigned int>(p);return (unsigned short)(val ^ (val >> 16));
}// C風(fēng)格強(qiáng)制類型轉(zhuǎn)換
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 轉(zhuǎn)換不執(zhí)行運(yùn)行時(shí)檢查。//由 pb 指向的對(duì)象可能不是 D 類型的對(duì)象,在這種情況下使用 *pd2 會(huì)是災(zāi)難性的。//例如,調(diào)用 D 類(而非 B 類)的成員函數(shù)可能會(huì)導(dǎo)致訪問(wèn)沖突。D1* pd2 = static_cast<D1*>(pb); // Not safe, D can have fields and methods that are not in B.B1* pb2 = static_cast<B1*>(pd); // Safe conversion, D always contains all of B.
}void test_static_cast3(B1* pb)
{//如果 pb 確實(shí)指向 D 類型的對(duì)象,則 pd1 和 pd2 將獲取相同的值。如果 pb == 0,它們也將獲取相同的值。//如果 pb 指向 B 類型的對(duì)象,而非指向完整的 D 類,則 dynamic_cast 足以判斷返回零。//但是,static_cast 依賴于程序員的斷言,即 pb 指向 D 類型的對(duì)象,因而只是返回指向那個(gè)假定的 D 對(duì)象的指針。D1* pd1 = dynamic_cast<D1*>(pb);D1* pd2 = static_cast<D1*>(pb);
}void test_static_cast4()
{char ch;int i = 65;float f = 2.5;double dbl;ch = static_cast<char>(i); // int to chardbl = static_cast<double>(f); // float to doublei = static_cast<int>(ch);
}void test_static_cast5()
{CCTest X;X.setNumber(8);X.printNumber();
}void test_static_cast6(D2* pd)
{//此轉(zhuǎn)換類型稱為“向上轉(zhuǎn)換”,因?yàn)樗鼘⒃陬悓哟谓Y(jié)構(gòu)上的指針,從派生的類移到該類派生的類。向上轉(zhuǎn)換是一種隱式轉(zhuǎn)換。C2* pc = dynamic_cast<C2*>(pd); // ok: C is a direct base class pc points to C subobject of pd B2* pb = dynamic_cast<B2*>(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<void*>(pa);// pv now points to an object of type Apv = dynamic_cast<void*>(pb);// pv now points to an object of type B
}void test_static_cast8()
{B4* pb = new D4; // unclear but okB4* pb2 = new B4;//此轉(zhuǎn)換類型稱為“向下轉(zhuǎn)換”,因?yàn)樗鼘⒃陬悓哟谓Y(jié)構(gòu)下的指針,從給定的類移到該類派生的類。D4* pd = dynamic_cast<D4*>(pb); // ok: pb actually points to a DD4* pd2 = dynamic_cast<D4*>(pb2); // pb2 points to a B not a D
}void test_static_cast9()
{A3* pa = new A3;B3* pb = dynamic_cast<B3*>(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;}
}
主要參考文獻(xiàn):
1. https://msdn.microsoft.com/zh-cn/library/c36yw7x9.aspx
2.?http://en.cppreference.com/w/cpp/language/static_cast?
3.?http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used?
GitHub:github.com/fengbingchun/Messy_Test
總結(jié)
以上是生活随笔為你收集整理的C++中static_cast/const_cast/dynamic_cast/reinterpret_cast的区别和使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C/C++中inline/static
- 下一篇: C++中istream的使用