【C++ 筆記】型態轉換運算子(Casting Operator) === 目錄(Table of Contents): [TOC] --- 很感謝你點進來這篇文章。 你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧! 四大運算子 --- 稍後要講的這些運算子都是所謂的顯式轉換,表示有明確的函式去強制轉換資料型態,反之則為隱式轉換,如 `10 / 1.0` 自動轉成 float。 C-style 的顯式轉換:`(int)var`。 在 C++ 用 casting operator 取代,也更為安全。 ### 1. static_cast 編譯時才轉換,因此在執行過程中沒有任何的效能開銷。 語法: ```cpp static_cast <new_type> (exp); ``` exp:要轉換的資料。 new_type:所需的運算式型態。 > The static_cast can be used to convert between related types, such as numeric types or pointers in the same inheritance hierarchy. > From GeeksForGeeks static_cast 可用在相關型態之間轉換,如:相同繼承層次結構中的數字型態或指標。 相關型態指的是 `int, float, double, char` 等等這種可以互相轉換的型態,像是 `'A'` 是 char,但他轉成 ascii 碼後就是一個數字而已,可以轉成 int,數值範圍也處於 int 的合法範圍內。 如下範例就是 double 轉 int: ```cpp= double pi = 3.14159; int x = static_cast<int>(pi); // 將 double 轉為 int ``` 註:可使用 `typeid()` 運算子去檢查當前的資料型態,確認是否真的有轉換,如上範例轉換後,經 `typeid()` 回傳值應為 `i`,表示 int。 第二個「繼承層次中基底類別與衍生類的轉換」: - 可以將「衍生類指標」轉型為「基類指標」。 - 或在已知物件實際型態的前提下,反向轉為衍生類指標(需小心)。 有關第二點: ```cpp= class Base {}; class Derived : public Base {}; Derived d; Base* b = static_cast<Base*>(&d); // 衍生類 -> 基類(安全) ``` 若要將 `Base*` 強制轉為 `Derived*`,只有在你確信該物件實際上是 Derived 型態時才可行,否則會發生未定義行為(Undefined behavior),這時應使用 `dynamic_cast`(這東西是另一個 casting operator,稍後介紹)較安全。 ### 2. dynamic_cast 主要用於向下轉型(基類指標 -> 派生類指標)於多型及繼承中。 語法: ```cpp dynamic_cast <new_type> (exp); ``` 如果轉不了,就回傳 `nullptr`,或 throws bad_cast 例外處理(對於參考轉換上來說)。 限制: - 僅適用於多型(基類需有虛函數)。 - 執行時帶來額外效能開銷。 範例: ```cpp= #include <iostream> using namespace std; class Animal { public: virtual void speak() { cout << "Animal speaks." << endl; } }; class Dog : public Animal { public: void speak() override { cout << "Woof! Woof!" << endl; } void wagTail() { cout << "Dog is wagging its tail." << endl; } }; int main() { Animal* pet = new Dog(); // 將 Animal* 轉型為 Dog* Dog* dogPtr = dynamic_cast<Dog*>(pet); if (dogPtr) { dogPtr->speak(); // 呼叫 Dog 的函式 dogPtr->wagTail(); // 使用 Dog 特有的方法 } else { cout << "轉型失敗,不是 Dog!" << endl; } delete pet; return 0; } ``` Output: ``` Woof! Woof! Dog is wagging its tail. ``` ### 3. const_cast 該運算子用於修改變數的 const 或 volatile 修飾子。可讓程式設計師暫時移除物件的 const 性質,並做修改。 在用這個運算子的時候需要很小心,在修改原為 const 的物件時很可能導致未定義行為。 語法: ```cpp const_cast <new_type> (exp); ``` 範例: ```cpp= #include <iostream> using namespace std; void func(const int* ptr) { cout << "原本的值: " << *ptr << endl; // const_cast 去除 const 屬性 int* modifiablePtr = const_cast<int*>(ptr); *modifiablePtr = 100; // 修改值 cout << "修改後的值: " << *modifiablePtr << endl; } int main() { int x = 42; const int* ptr = &x; func(ptr); cout << "main 中的 x: " << x << endl; return 0; } ``` Output: ``` 原本的值: 42 修改後的值: 100 main 中的 x: 100 ``` 如果指向的是 `const int x = 42;`,這樣的 `const_cast` 修改會造成未定義行為。 因此修改成功的前提是:該物件(`x`)本身不是 `const`,只是被 `const` 指標包起來,這種修改是可行的。 ### 4. reinterpret_cast 該轉換是最不安全的一種,因此在使用上要相當小心(很常實作時會發生未定義行為)。 主要用於彼此無關的型態之間轉換的方法,如在指標、整數、函數指標之間的轉換。不會檢查轉換後的指標是否屬於相同型態。 語法與上面三個都一樣,就不寫了。 範例: ```cpp= #include <iostream> using namespace std; int main() { int a = 65; // 將整數 a 的位址轉型為 char*,再取出內容 char* ptr = reinterpret_cast<char*>(&a); cout << "整數 a 的值為: " << a << endl; cout << "reinterpret_cast 後,ptr 指向的 char 值為: " << *ptr << endl; return 0; } ``` Output: ``` 整數 a 的值為: 65 reinterpret_cast 後,ptr 指向的 char 值為: A ``` 參考資料 --- [算術運算、型態轉換](https://openhome.cc/Gossip/CppGossip/ArithmeticOperator.html) [Casting Operators in C++ - GeeksforGeeks](https://www.geeksforgeeks.org/casting-operators-in-cpp/)