【C++ 筆記】型態轉換運算子(Casting Operator)

很感謝你點進來這篇文章。

你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧!

四大運算子

稍後要講的這些運算子都是所謂的顯式轉換,表示有明確的函式去強制轉換資料型態,反之則為隱式轉換,如 10 / 1.0 自動轉成 float。

C-style 的顯式轉換:(int)var

在 C++ 用 casting operator 取代,也更為安全。

1. static_cast

編譯時才轉換,因此在執行過程中沒有任何的效能開銷。

語法:

1
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:

1
2
double pi = 3.14159;
int x = static_cast<int>(pi); // 將 double 轉為 int

註:可使用 typeid() 運算子去檢查當前的資料型態,確認是否真的有轉換,如上範例轉換後,經 typeid() 回傳值應為 i,表示 int。

第二個「繼承層次中基底類別與衍生類的轉換」:

  • 可以將「衍生類指標」轉型為「基類指標」。
  • 或在已知物件實際型態的前提下,反向轉為衍生類指標(需小心)。

有關第二點:

1
2
3
4
5
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

主要用於向下轉型(基類指標 -> 派生類指標)於多型及繼承中。

語法:

1
dynamic_cast <new_type> (exp);

如果轉不了,就回傳 nullptr,或 throws bad_cast 例外處理(對於參考轉換上來說)。

限制:

  • 僅適用於多型(基類需有虛函數)。
  • 執行時帶來額外效能開銷。

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#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:

1
2
Woof! Woof!
Dog is wagging its tail.

3. const_cast

該運算子用於修改變數的 const 或 volatile 修飾子。可讓程式設計師暫時移除物件的 const 性質,並做修改。

在用這個運算子的時候需要很小心,在修改原為 const 的物件時很可能導致未定義行為。

語法:

1
const_cast <new_type> (exp);

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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:

1
2
3
原本的值: 42
修改後的值: 100
main 中的 x: 100

如果指向的是 const int x = 42;,這樣的 const_cast 修改會造成未定義行為。

因此修改成功的前提是:該物件(x)本身不是 const,只是被 const 指標包起來,這種修改是可行的。

4. reinterpret_cast

該轉換是最不安全的一種,因此在使用上要相當小心(很常實作時會發生未定義行為)。

主要用於彼此無關的型態之間轉換的方法,如在指標、整數、函數指標之間的轉換。不會檢查轉換後的指標是否屬於相同型態。

語法與上面三個都一樣,就不寫了。

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#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:

1
2
整數 a 的值為: 65
reinterpret_cast 後,ptr 指向的 char 值為: A

參考資料

算術運算、型態轉換

Casting Operators in C++ - GeeksforGeeks