[英]Are there any C++ tools that detect misuse of static_cast, dynamic_cast, and reinterpret_cast?
[英]When should static_cast, dynamic_cast, const_cast, and reinterpret_cast be used?
什么是正確的用途:
static_cast
dynamic_cast
const_cast
reinterpret_cast
(type)value
(C 風格轉換)type(value)
(函數式轉換)如何決定在哪些特定情況下使用哪個?
static_cast
是您應該嘗試使用的第一個演員表。 它執行諸如類型之間的隱式轉換(例如int
到float
或指向void*
的指針)之類的事情,它還可以調用顯式轉換函數(或隱式轉換函數)。 在許多情況下,不需要顯式聲明static_cast
,但重要的是要注意T(something)
語法等同於(T)something
並且應該避免使用(稍后會詳細介紹)。 然而,一個T(something, something_else)
是安全的,並且保證調用構造函數。
static_cast
也可以通過繼承層次結構進行轉換。 向上轉換(朝向基類)時沒有必要,但向下轉換時可以使用它,只要它不通過virtual
繼承進行轉換即可。 但是,它不進行檢查,將層次結構向下轉換為實際上不是對象類型的類型是static_cast
的行為。
const_cast
可用於刪除或添加const
到變量; 沒有其他 C++ 演員能夠刪除它(甚至reinterpret_cast
也不行)。 需要注意的是,只有當原始變量是const
時,修改以前的const
值才是未定義的; 如果您使用它來取消const
對未使用const
聲明的內容的引用,則它是安全的。 例如,當基於const
重載成員函數時,這可能很有用。 也可以用來給對象添加const
,比如調用成員函數重載。
const_cast
也同樣適用於volatile
,盡管這種情況不太常見。
dynamic_cast
專門用於處理多態性。 您可以將指向任何多態類型的指針或引用強制轉換為任何其他類類型(多態類型至少有一個聲明或繼承的虛函數)。 您不僅可以將其用於向下投射 - 您還可以向側面投射,甚至可以向上投射另一條鏈。 dynamic_cast
將尋找所需的對象並在可能的情況下返回它。 如果不能,它會在指針的情況下返回nullptr
,或者在引用的情況下拋出std::bad_cast
。
不過, dynamic_cast
有一些限制。 如果繼承層次結構中有多個相同類型的對象(所謂的“可怕的菱形”)並且您沒有使用virtual
繼承,則它不起作用。 它也只能通過公共繼承——它總是無法通過protected
或private
繼承。 然而,這很少成為問題,因為這種繼承形式很少見。
reinterpret_cast
是最危險的演員,應該非常謹慎地使用。 它將一種類型直接轉換為另一種類型——例如將值從一個指針轉換為另一個,或將指針存儲在int
中,或各種其他令人討厭的事情。 很大程度上,使用reinterpret_cast
獲得的唯一保證是,通常如果將結果轉換回原始類型,您將獲得完全相同的值(但如果中間類型小於原始類型,則不會)。 也有許多reinterpret_cast
無法進行的轉換。 它主要用於特別奇怪的轉換和位操作,例如將原始數據流轉換為實際數據,或將數據存儲在指向對齊數據的指針的低位中。
C 風格轉換和函數風格轉換分別使用(type)object
或type(object)
進行轉換,並且在功能上是等效的。 它們被定義為以下成功的第一個:
const_cast
static_cast
(盡管忽略了訪問限制)static_cast
(見上文),然后是const_cast
reinterpret_cast
reinterpret_cast
,然后是const_cast
因此,它可以在某些情況下用作其他強制轉換的替代品,但由於能夠演變為reinterpret_cast
,因此可能非常危險,並且當需要顯式強制轉換時應該首選后者,除非您確定static_cast
會成功或reinterpret_cast
將失敗。 即便如此,考慮更長、更明確的選項。
C 風格的轉換在執行static_cast
時也會忽略訪問控制,這意味着它們能夠執行其他轉換無法執行的操作。 不過,這主要是一個雜物,在我看來,這只是避免 C 風格轉換的另一個原因。
使用dynamic_cast
在繼承層次結構中轉換指針/引用。
使用static_cast
進行普通類型轉換。
使用reinterpret_cast
對位模式進行低級重新解釋。 使用時要格外小心。
使用const_cast
丟棄const/volatile
。 避免這種情況,除非您使用 const 不正確的 API 被卡住。
(上面已經給出了很多理論和概念上的解釋)
以下是我使用static_cast 、 dynamic_cast 、 const_cast 、 reinterpret_cast時的一些實際示例。
(也參考這個來理解解釋: http ://www.cplusplus.com/doc/tutorial/typecasting/)
靜態轉換:
OnEventData(void* pData)
{
......
// pData is a void* pData,
// EventData is a structure e.g.
// typedef struct _EventData {
// std::string id;
// std:: string remote_id;
// } EventData;
// On Some Situation a void pointer *pData
// has been static_casted as
// EventData* pointer
EventData *evtdata = static_cast<EventData*>(pData);
.....
}
動態轉換:
void DebugLog::OnMessage(Message *msg)
{
static DebugMsgData *debug;
static XYZMsgData *xyz;
if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
// debug message
}
else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
// xyz message
}
else/* if( ... )*/{
// ...
}
}
const_cast :
// *Passwd declared as a const
const unsigned char *Passwd
// on some situation it require to remove its constness
const_cast<unsigned char*>(Passwd)
reinterpret_cast :
typedef unsigned short uint16;
// Read Bytes returns that 2 bytes got read.
bool ByteBuffer::ReadUInt16(uint16& val) {
return ReadBytes(reinterpret_cast<char*>(&val), 2);
}
如果您了解一點內部知識可能會有所幫助...
static_cast
float
和int
之類的縮放器類型之間進行轉換。 為他們使用static_cast
。A
轉換為B
時, static_cast
調用B
的構造函數,將A
作為參數傳遞。 或者, A
可以有一個轉換運算符(即A::operator B()
)。 如果B
沒有這樣的構造函數,或者A
沒有轉換運算符,則會出現編譯時錯誤。A*
轉換為B*
始終成功,否則會出現編譯錯誤。A&
到B&
也是如此。dynamic_cast
(Base*)
到(Derived*)
可能會失敗。A*
到B*
,如果強制轉換無效,則 dynamic_cast 將返回 nullptr。A&
到B&
如果轉換無效,則 dynamic_cast 將拋出 bad_cast 異常。const_cast
set<T>
這樣的容器,它只返回它的元素作為 const 以確保你不會改變它的鍵。 但是,如果您的意圖是修改對象的非關鍵成員,那么應該沒問題。 您可以使用 const_cast 刪除 constness。T& SomeClass::foo()
以及const T& SomeClass::foo() const
時。 為避免代碼重復,您可以應用 const_cast 從另一個函數返回一個函數的值。reinterpret_cast
float
加載到 4 字節的int
中,以查看float
中的位是什么樣子。除了到目前為止的其他答案之外,這是一個不明顯的例子,其中static_cast
不夠,因此需要reinterpret_cast
。 假設有一個函數在輸出參數中返回指向不同類(不共享公共基類)對象的指針。 這種函數的一個真實例子是CoCreateInstance()
(見最后一個參數,實際上是void**
)。 假設您從該函數請求特定類的對象,因此您事先知道指針的類型(您經常為 COM 對象執行此操作)。 在這種情況下,您不能使用static_cast
將指針轉換為void**
:您需要reinterpret_cast<void**>(&yourPointer)
。
在代碼中:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
//static_cast<void**>(&pNetFwPolicy2) would give a compile error
reinterpret_cast<void**>(&pNetFwPolicy2) );
但是, static_cast
適用於簡單指針(不是指向指針的指針),因此可以通過以下方式重寫上述代碼以避免reinterpret_cast
(以額外變量為代價):
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
&tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);
static_cast
vs dynamic_cast
vs reinterpret_cast
internals view on a downcast/upcast
在這個答案中,我想在一個具體的向上/向下轉換示例中比較這三種機制,並分析底層指針/內存/程序集發生了什么,以具體了解它們的比較方式。
我相信這將對這些演員的不同之處提供一個很好的直覺:
static_cast
:在運行時進行一個地址偏移(運行時影響低),並且沒有安全檢查向下轉換是否正確。
dyanamic_cast
:在運行時執行與static_cast
相同的地址偏移,但也使用 RTTI 進行昂貴的安全檢查,以確保向下轉換是正確的。
此安全檢查允許您通過檢查指示無效向下轉換的nullptr
返回來查詢基類指針是否在運行時屬於給定類型。
因此,如果您的代碼無法檢查該nullptr
並采取有效的非中止操作,您應該只使用static_cast
而不是動態轉換。
如果中止是您的代碼可以采取的唯一操作,那么您可能只想在調試版本 ( -NDEBUG
) 中啟用dynamic_cast
,否則使用static_cast
,例如,如此處所做的,以免減慢您的快速運行。
reinterpret_cast
:在運行時什么都不做,甚至地址偏移也不做。 指針必須准確地指向正確的類型,甚至基類都不起作用。 除非涉及原始字節流,否則您通常不希望這樣做。
考慮以下代碼示例:
主文件
#include <iostream>
struct B1 {
B1(int int_in_b1) : int_in_b1(int_in_b1) {}
virtual ~B1() {}
void f0() {}
virtual int f1() { return 1; }
int int_in_b1;
};
struct B2 {
B2(int int_in_b2) : int_in_b2(int_in_b2) {}
virtual ~B2() {}
virtual int f2() { return 2; }
int int_in_b2;
};
struct D : public B1, public B2 {
D(int int_in_b1, int int_in_b2, int int_in_d)
: B1(int_in_b1), B2(int_in_b2), int_in_d(int_in_d) {}
void d() {}
int f2() { return 3; }
int int_in_d;
};
int main() {
B2 *b2s[2];
B2 b2{11};
D *dp;
D d{1, 2, 3};
// The memory layout must support the virtual method call use case.
b2s[0] = &b2;
// An upcast is an implicit static_cast<>().
b2s[1] = &d;
std::cout << "&d " << &d << std::endl;
std::cout << "b2s[0] " << b2s[0] << std::endl;
std::cout << "b2s[1] " << b2s[1] << std::endl;
std::cout << "b2s[0]->f2() " << b2s[0]->f2() << std::endl;
std::cout << "b2s[1]->f2() " << b2s[1]->f2() << std::endl;
// Now for some downcasts.
// Cannot be done implicitly
// error: invalid conversion from ‘B2*’ to ‘D*’ [-fpermissive]
// dp = (b2s[0]);
// Undefined behaviour to an unrelated memory address because this is a B2, not D.
dp = static_cast<D*>(b2s[0]);
std::cout << "static_cast<D*>(b2s[0]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = static_cast<D*>(b2s[1]);
std::cout << "static_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Segfault because dp is nullptr.
dp = dynamic_cast<D*>(b2s[0]);
std::cout << "dynamic_cast<D*>(b2s[0]) " << dp << std::endl;
//std::cout << "dynamic_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = dynamic_cast<D*>(b2s[1]);
std::cout << "dynamic_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "dynamic_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Undefined behaviour to an unrelated memory address because this
// did not calculate the offset to get from B2* to D*.
dp = reinterpret_cast<D*>(b2s[1]);
std::cout << "reinterpret_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "reinterpret_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
}
編譯、運行和反匯編:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
setarch `uname -m` -R ./main.out
gdb -batch -ex "disassemble/rs main" main.out
其中setarch
用於禁用 ASLR ,以便更輕松地比較運行。
可能的輸出:
&d 0x7fffffffc930
b2s[0] 0x7fffffffc920
b2s[1] 0x7fffffffc940
b2s[0]->f2() 2
b2s[1]->f2() 3
static_cast<D*>(b2s[0]) 0x7fffffffc910
static_cast<D*>(b2s[0])->int_in_d 1
static_cast<D*>(b2s[1]) 0x7fffffffc930
static_cast<D*>(b2s[1])->int_in_d 3
dynamic_cast<D*>(b2s[0]) 0
dynamic_cast<D*>(b2s[1]) 0x7fffffffc930
dynamic_cast<D*>(b2s[1])->int_in_d 3
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
reinterpret_cast<D*>(b2s[1])->int_in_d 32767
現在,正如在: https ://en.wikipedia.org/wiki/Virtual_method_table 中提到的,為了有效地支持虛方法調用,假設 B1 的內存數據結構的形式為:
B1:
+0: pointer to virtual method table of B1
+4: value of int_in_b1
B2
的形式為:
B2:
+0: pointer to virtual method table of B2
+4: value of int_in_b2
那么D
的內存數據結構必須看起來像:
D:
+0: pointer to virtual method table of D (for B1)
+4: value of int_in_b1
+8: pointer to virtual method table of D (for B2)
+12: value of int_in_b2
+16: value of int_in_d
關鍵事實是D
的內存數據結構內部包含與B1
和B2
相同的內存結構,即:
int_in_b1
int_in_b2
因此,我們得出關鍵結論:
向上轉換或向下轉換只需將指針值移動編譯時已知的值
這樣,當D
被傳遞給基本類型數組時,類型轉換實際上會計算該偏移量並指向看起來與內存中有效B2
完全一樣的東西,除了這個具有D
的 vtable 而不是B2
,因此所有虛擬通話透明地工作。
例如:
b2s[1] = &d;
只需要獲取d
+8的地址就可以到達對應的B2-like數據結構。
現在,我們終於可以回到類型轉換和具體示例的分析上了。
從標准輸出輸出我們看到:
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
因此,在那里完成的隱式static_cast
確實正確地計算了從 0x7fffffffc930 處的完整D
數據結構到B2
的偏移量,例如 0x7fffffffc940 處的偏移量。 我們還推斷,介於 0x7fffffffc930 和 0x7fffffffc940 之間的可能是B1
數據和 vtable。
然后,在 downcast 部分,現在很容易理解無效部分如何失敗以及為什么:
static_cast<D*>(b2s[0]) 0x7fffffffc910
:編譯器剛剛在編譯時上升 0x10 字節以嘗試從B2
轉到包含D
但是因為b2s[0]
不是D
,它現在指向一個未定義的內存區域。
拆解是:
49 dp = static_cast<D*>(b2s[0]); 0x0000000000000fc8 <+414>: 48 8b 45 d0 mov -0x30(%rbp),%rax 0x0000000000000fcc <+418>: 48 85 c0 test %rax,%rax 0x0000000000000fcf <+421>: 74 0a je 0xfdb <main()+433> 0x0000000000000fd1 <+423>: 48 8b 45 d0 mov -0x30(%rbp),%rax 0x0000000000000fd5 <+427>: 48 83 e8 10 sub $0x10,%rax 0x0000000000000fd9 <+431>: eb 05 jmp 0xfe0 <main()+438> 0x0000000000000fdb <+433>: b8 00 00 00 00 mov $0x0,%eax 0x0000000000000fe0 <+438>: 48 89 45 98 mov %rax,-0x68(%rbp)
所以我們看到 GCC 確實:
D
dynamic_cast<D*>(b2s[0]) 0
: C++ 實際上發現強制轉換無效並返回nullptr
!
這不可能在編譯時完成,我們將從反匯編中確認:
59 dp = dynamic_cast<D*>(b2s[0]); 0x00000000000010ec <+706>: 48 8b 45 d0 mov -0x30(%rbp),%rax 0x00000000000010f0 <+710>: 48 85 c0 test %rax,%rax 0x00000000000010f3 <+713>: 74 1d je 0x1112 <main()+744> 0x00000000000010f5 <+715>: b9 10 00 00 00 mov $0x10,%ecx 0x00000000000010fa <+720>: 48 8d 15 f7 0b 20 00 lea 0x200bf7(%rip),%rdx # 0x201cf8 <_ZTI1D> 0x0000000000001101 <+727>: 48 8d 35 28 0c 20 00 lea 0x200c28(%rip),%rsi # 0x201d30 <_ZTI2B2> 0x0000000000001108 <+734>: 48 89 c7 mov %rax,%rdi 0x000000000000110b <+737>: e8 c0 fb ff ff callq 0xcd0 <__dynamic_cast@plt> 0x0000000000001110 <+742>: eb 05 jmp 0x1117 <main()+749> 0x0000000000001112 <+744>: b8 00 00 00 00 mov $0x0,%eax 0x0000000000001117 <+749>: 48 89 45 98 mov %rax,-0x68(%rbp)
首先進行 NULL 檢查,如果輸入為 NULL,則返回 NULL。
否則,它會在 RDX、RSI 和 RDI 中設置一些參數並調用__dynamic_cast
。
我現在沒有耐心對此進行進一步分析,但正如其他人所說,唯一可行的方法是讓__dynamic_cast
訪問一些表示類層次結構的額外 RTTI 內存數據結構。
因此,它必須從該表的B2
條目開始,然后遍歷該類層次結構,直到找到來自b2s[0]
的D
類型轉換的 vtable。
這就是為什么動態轉換可能很昂貴的原因! 這是一個示例,其中在復雜項目中將dynamic_cast
轉換為static_cast
的單行補丁將運行時間減少了 33%! .
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
這個只是盲目相信我們:我們說地址b2s[1]
有一個D
,編譯器不進行偏移計算。
但這是錯誤的,因為D實際上在0x7fffffffc930,0x7fffffffc940是D內部的類似B2的結構! 所以垃圾被訪問了。
我們可以從可怕的-O0
程序集中確認這一點,它只是移動了值:
70 dp = reinterpret_cast<D*>(b2s[1]); 0x00000000000011fa <+976>: 48 8b 45 d8 mov -0x28(%rbp),%rax 0x00000000000011fe <+980>: 48 89 45 98 mov %rax,-0x68(%rbp)
相關問題:
在 Ubuntu 18.04 amd64、GCC 7.4.0 上測試。
雖然其他答案很好地描述了 C++ 強制轉換之間的所有差異,但我想添加一個簡短說明,為什么您不應該使用 C 風格強制轉換(Type) var
和Type(var)
。
對於 C++ 初學者來說,C 樣式轉換看起來像是 C++ 轉換(static_cast<>()、dynamic_cast<>()、const_cast<>()、reinterpret_cast<>())的超集操作,有人可能更喜歡它們而不是 C++ 轉換. 事實上,C 風格的演員表是超集並且寫起來更短。
C 風格轉換的主要問題是它們隱藏了開發人員轉換的真實意圖。 C 風格的轉換幾乎可以執行所有類型的轉換,從由 static_cast<>() 和 dynamic_cast<>() 完成的正常安全轉換到像 const_cast<>() 這樣的潛在危險轉換,其中可以刪除 const 修飾符,因此 const 變量可以修改和 reinterpret_cast<>() 甚至可以將整數值重新解釋為指針。
這是示例。
int a=rand(); // Random number.
int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.
int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.
int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.
*pa4=5; // Program crashes.
將 C++ 強制轉換添加到語言中的主要原因是允許開發人員闡明他的意圖——他為什么要進行這種強制轉換。 通過使用在 C++ 中完全有效的 C 樣式轉換,您的代碼可讀性降低並且更容易出錯,特別是對於沒有創建您的代碼的其他開發人員而言。 因此,為了使您的代碼更具可讀性和明確性,您應該始終更喜歡 C++ 強制轉換而不是 C 樣式強制轉換。
這是 Bjarne Stroustrup(C++ 的作者)的書 The C++ Programming Language 4th edition - page 302 的簡短引述。
這種 C 風格的轉換比命名轉換運算符危險得多,因為這種符號在大型程序中更難發現,而且程序員想要的轉換類型並不明確。
為了理解,讓我們考慮下面的代碼片段:
struct Foo{};
struct Bar{};
int main(int argc, char** argv)
{
Foo* f = new Foo;
Bar* b1 = f; // (1)
Bar* b2 = static_cast<Bar*>(f); // (2)
Bar* b3 = dynamic_cast<Bar*>(f); // (3)
Bar* b4 = reinterpret_cast<Bar*>(f); // (4)
Bar* b5 = const_cast<Bar*>(f); // (5)
return 0;
}
只有第 (4) 行編譯沒有錯誤。 只有reinterpret_cast可用於將指向對象的指針轉換為指向任何不相關對象類型的指針。
需要注意的一點是: dynamic_cast在運行時會失敗,但是在大多數編譯器上它也將無法編譯,因為被強制轉換的指針的結構中沒有虛函數,這意味着dynamic_cast僅適用於多態類指針.
何時使用 C++ cast :
reinterpret_cast
的一個不錯的特性,在其他答案中沒有提到,它允許我們為函數類型創建一種void*
指針。 通常,對於對象類型,使用static_cast
來檢索存儲在void*
中的指針的原始類型:
int i = 13;
void *p = &i;
auto *pi = static_cast<int*>(p);
對於函數,我們必須使用reinterpret_cast
兩次:
#include<iostream>
using any_fcn_ptr_t = void(*)();
void print(int i)
{
std::cout << i <<std::endl;
}
int main()
{
//Create type-erased pointer to function:
auto any_ptr = reinterpret_cast<any_fcn_ptr_t>(&print);
//Retrieve the original pointer:
auto ptr = reinterpret_cast< void(*)(int) >(any_ptr);
ptr(7);
}
使用reinterpret_cast
,我們甚至可以獲得類似的 sort-of-void* 指針,用於指向成員函數的指針。
與普通的void*
和static_cast
一樣,C++ 保證ptr
指向print
函數(只要我們將正確的類型傳遞給reinterpret_cast
)。
我們通過一個例子來看看reinterpret_cast
和static_cast
的區別:
#include <iostream>
using namespace std;
class A
{
int a;
};
class B
{
int b;
};
class C : public A, public B
{
int c;
};
int main()
{
{
B b;
cout << &b << endl;
cout << static_cast<C *>(&b) << endl; // 1
cout << reinterpret_cast<C *>(&b) << endl; // 2
}
cout << endl;
{
C c;
cout << &c << endl;
cout << static_cast<B *>(&c) << endl; // 3
cout << reinterpret_cast<B *>(&c) << endl; // 4
}
cout << endl;
{
A a;
cout << &a << endl;
cout << static_cast<C *>(&a) << endl;
cout << reinterpret_cast<C *>(&a) << endl;
}
cout << endl;
{
C c;
cout << &c << endl;
cout << static_cast<A *>(&c) << endl;
cout << reinterpret_cast<A *>(&c) << endl;
}
return 0;
}
產生 output:
0x7ffcede34f0c
0x7ffcede34f08 // 1
0x7ffcede34f0c // 2
0x7ffcede34f0c
0x7ffcede34f10 // 3
0x7ffcede34f0c // 4
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c
請注意 output 1
和2
以及3
和4
不同。 這是為什么? 其中一個是static_cast
,另一個是reinterpret_cast
在這兩種情況下對相同輸入的相同類型。
情況可以用下圖形象化:
C
包含一個B
但B
的起始地址與C
。 static_cast
正確計算B
在C
內的地址。 然而, reinterpret_cast
返回的地址與我們輸入的地址相同,這對於這種情況是不正確的:該地址沒有B
但是,在A
和C
指針之間轉換時,兩種轉換都返回相同的結果,因為它們碰巧從相同的位置開始,順便說一句,標准並不能保證。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.