Re: [問題] static.const.reinterpret_cast和c-sty …

看板C_and_CPP (C/C++)作者 (cppOrz)時間18年前 (2006/03/12 20:31), 編輯推噓2(200)
留言2則, 2人參與, 最新討論串1/1
Sorry, 有很多地方錯誤,不得不指出來。 熱心回答問題是好的,但是最好對內容有一定的把握,以免誤導。 這一篇介紹 C++ 的四個關鍵字: static_cast, const_cast, reinterpret_cast, 以及 dynamic_cast 的意義用法。 學習任何編程語言,只單純學習語法是不夠的,要適當的運用某種語言機制,最好 要先知道該機制存在的目的,和什麼狀況下會需要用到它,否則還不如不要使用。 ※ 引述《godfat (godfat 真常)》之銘言: : 靜態轉型,簡單地說就是強制轉型, : 只是不合理的轉型 compiler 會告訴你不能這樣轉 : 例如兩個毫無關聯的物件 : class A{}; : class B{}; : A a; : static_cast<B>(a); // error static_cast 用於「相關型別」之間的轉換,其目的是讓編譯器知道, 程序員是「有意識」的進行轉換動作,並非不小心打錯。例如: float f = 123.456f; int i = f; // 把 float 轉為 int,編譯器一般會給警告 int v = static_cast<int>(f); // 用 static_cast 通知編譯器,設計意途確實如此 此外 static_cast 也可用於繼承體系內的 downcast,例如: struct B { void f(); }; struct D : B { void g(); } d; B &b = d; ... b.f(); // ok b.g(); // 錯誤,b 是 reference to B 物件 static_cast<D&>(b).g(); // ok, 因為程序員知道 b 確實是參照某個 D 物件 : : const_cast : 單純去掉物件的常數性 和/或 volatile 性 這裏是對的。 : int const i = 10; : i = 5; // error : const_cast<int&>(i) = 5; // ok : (這邊我不太確定是不是這樣寫,很少用) : (總之觀念是這樣) 舉例有誤。 const_cast 只是把常數性去掉,目的是方便設計,但是企圖通過 const_cast 修改一個 const 物件,其結果未定義(視實作環境是否對該 const 物件採取 保護措施)。 至於 const_cast 使用的時機,通常是為了在不修改既有模組(例如沒有 source code 的程式庫)的情況下,解決兩個模組之間 const-correctness 不相容的問題 (通常是不能改的那個模組,其設計不夠周延)。例如: struct C { void f(); }; // 假設 C 是一個既存的模組(不能修改) void foo(C const &c) // foo 是自行設計的模組,c 物件只作為輸入,不會被更動 { ... c.f(); // 由於 c 是 referece to const C 物件,但 C::f 並未被設計為 // const,因此這樣寫是不成立的。(編譯器會給錯誤或至少警告) const_cast<C&>(c).f(); // ok, 用 const_cast 去掉 c 的常數性 } : : reinterpret_cast : 強迫 compiler 把輸入型別視為欲轉換的型別 : 這個不太好解釋…總之就是暴力轉型就對了 XD : 直接把該記憶體位置的資料視為欲轉換的型別來看待 reinterpret_cast 幾乎等於暴力轉型(指 C-style 轉型),但不能去掉物件 的常數性(也就是還不夠暴力)。它用來處理任意型別之間的轉換,通常是為 了爭取運算或儲存空間的效率時,所採取的低階操作。例如: int IP_Addr = 0; char *p = reinterpret_cast<char*>(&IP_Addr); // 轉型為 char *,以便於 // 以 Byte 為單位來處理 p[0] = 192; p[1] = 168; p[2] = 0; p[3] = 1; 傳統的 C-style 轉型相當於 static_cast, const_cast, reinterpret_cast 三合一(也就是暴力加三級)。 : 你漏了 dynamic_cast<> : 這跟 static_cast<> 有些類似 dynamic_cast 和 static_cast 無關。 : dynamic_cast<> 只能對於具有多形性型別轉型, : 也就是他至少得要有一個 virtual function 這裏是對的。dynamic_cast 是 RTTI 的一部份,它用來檢測一個多型基底 類別的 pointer 或 reference 所參照物件的實際型別。例如: struct B1 { virtual void f() = 0; }; struct D1 { void f(); }; struct X : D1 { void f(); void g(); }; void foo(B1 *b1) // foo 模組並不知道 b1 所參照物件的實際型別 { if (dynamic_cast<D1*>(b1)) // 如果 b1 參照的是 D1 物件 { b1->f(); } else if (D1 *d1 = dynamic_cast<X*>(b1)) // 如果 b1 參照的是 X 物件 { d1->g(); } else { ... } } 此例中,dynamic_cast 的用法也是所謂的 downcast,但和 static_cast 不同的是,前者用於偵測未知多型物件的實際型別,而後者用於對已知物件 (可以是多型物件或普通物件)的轉型。 可以看出,B1, D1, X 的繼承體系設計並不是很理想,因為在這種情況下,應該 直接利用 virtual function 的動態多型機制,而非依賴 dynamic_cast 偵測物 件的型別。例如: struct B2 { virtual void f() = 0; virtual void g() = 0; }; struct D2 : B2 { void f(); void g(); } d2; struct Y : D2 { void f(); void g(); } y; void foo() { B2 *b2 = &d2; // b2 參照 D2 物件 b2->g(); // 實際上會執行 D2::g,不必依賴 dynamic_cast b2 = &y; // 換一下,改參照 Y 物件 b2->g(); // 實際上會執行 Y::g } 顯然,B2, D2, Y 的設計比較合理。事實上,如果所有的模組都是自行設計, dynamic_cast 是多餘的。但它之所以存在,其目的就是為了解決「既存模組 」不能修改的問題。(最常見的例子,就是缺乏源代碼的程式庫) 前面的例子中,如果 B1, D1 兩模組不能修改,為了新擴充 X 模組,又要和 舊模組相容,dynamic_cast 在此情況下就能派上用場(反過來說,如果沒有 dynamic_cast,問題就會變得很麻煩,而且各種替代方案都不太安全);但 如果完全是可自行控制的設計,就應該儘量遵循 B2, D2, Y 的方式來組織。 以上是 dynamic_cast 的功能中,downcast 的部份。另外,由於 C++ 支援 多重繼承的機制,dynamic_cast 亦可用於 crosscast(橫向轉型),例如: struct Z : B1, B2 // 多重繼承 { void f(); void g(); } z; void foo() { B1 *b1 = &z; B2 *b2 = dynamic_cast<B2*>(b1); // crosscast } 這個例子可以用來說明,dynamic_cast 實際上的動作是「型別檢測」,而非 僅僅是「轉型」,後者只是它輸出的結果。此例中,b1 表面上是 (B1 *),但 它實際上參照的是 Z 物件,因此 dynamic_cast 偵測的結果,就是它可以順 利轉為 (B2 *) 。 : struct Base{virtual ~Base(){}}; : struct Derived: public Base{}; : Base pb = new Derived; : Derived pd = dynamic_cast<Derived*>(pb); : 如果 pb 真的是指向 Derived, 則 dynamic_cast<> 傳回 pb 的地址 : 否的話,pd 為 NULL : boost 有提供 polymorphic_cast<>, 用處同 dynamic_cast<> : 差別在於錯誤時不是傳回 NULL, 而是丟出 std::bad_cast : polymorphic_downcast<> 則是專門用來在已知必然成功的 downcast : 這種時候內建的 static_cast<> 和 dynamic_cast<> 其實都可以用, : 只是 static_cast<> 沒有錯誤檢查,dynamic_cast<> 效率太差 : polymorphic_downcast<> 則使用 assert(); 來檢查是否成功 : http://www.boost.org/libs/conversion/cast.htm : : c-style cast : 等同於上面全部... : 在 C++ 中替他們分類,避免造成混淆 : 不過對初學者來說的話這麼多才是混淆吧,我猜 :p (下略) -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 59.120.214.120

03/13 01:44, , 1F
感謝指導,雖然我覺得跟我講的沒啥衝突 XD 大概是太隨便了
03/13 01:44, 1F
※ 編輯: cppOrz 來自: 59.120.214.120 (03/13 04:50) ※ crazying:轉錄至看板 NTUGIEE_EDA 03/13 11:58

03/14 18:16, , 2F
推一個~了解更深入了
03/14 18:16, 2F
文章代碼(AID): #1451IM68 (C_and_CPP)
文章代碼(AID): #1451IM68 (C_and_CPP)