一般情況下,語句(statement)是按順序執行的。
C++ 提供一組控制流程語句來處理更複雜的程式執行邏輯。如條件語句、循環語句與中斷當下控制流程的跳轉語句等。
當表達式末尾加上分號(;
)就成為語句。如:ival + 5;
。
ival + 5; // 沒有實際作用的語句。
std::cout << ival; // 有意義的表達式語句。
空語句只有一個分號(;
),為最簡單的語句。
; // 空語句。
用途:若程式在語法上需要一條語句,但邏輯實際上並不需要時,則可使用空語句。
// 持續讀入數據直至到達文件末尾或某次輸入的值等於sought
while(std::cin >> s && s != sought)
; // 空語句。
注意分號的使用:不要漏寫,也不要多寫,故多餘的語句不代表無害。
ival = v1 + v2;; // 正確,第二個分號表達一條多餘的語句。
while(iter != svec.end()); // while循環體為『空語句』。
++iter; // 遞增運算子並不屬於while循環的一部分。
複合語句(compound
statement)是指用『大括弧』括({}
)起來的(也可以為空)語句。又稱為『語句塊(block)』。
語句塊可視為一個『作用域(scope)』。
在語句塊內宣告的變數只能在該語句塊以及嵌套在該語句塊內被存取(可視 (visible))
若在程式某處,語法需要一條語句,但實際邏輯上需要多條語句,此時可使用『複合語句』。例如,while
語句或 for
語句:
while (val <= 10)
{
sum += val;
++val;
}
可在 if
、switch
、 while
或
for
語句內的控制結構內定義變數。而在控制結構內定義的變數只能在對應的語句內可視。若要在外部程式需要使用控制變數,則變數必須定義在外部。
while (int i = get_num())
std::cout << i << std::endl;
i = 0; // 錯誤,在while外部無法存取i。
auto beg = v.begin(); // 變數定義在外部。
while (beg != v.end() && *beg >= 0)
++beg;
if (beg == v.end())
{
}
if
語句根據條件來決定控制流程。
if
語句形式:
if (condition) // condition為真,則執行statement
statement
if (condition) // condition為真,則執行statement1,否則執行statement2。
statement1
else
statement2
condition 都必須被 ()
包起來。
condition 可以是表達式,也可以初始化一個變數。無論是哪一種,都必須能轉換為『布林值』。
statement、statement1 與 statement2 都必須為語句塊。
當單一條語句無法滿足邏輯需求時,可加入『大括弧』形成語句快。
當 if 語句嵌套在另一個 if 語句時,請留意 else 與哪一個 if 匹配為一組:
// 請注意實際程式執行狀況與程式縮排格式的差別:
if (grade % 10 >= 3)
if (grade % 10 > 7)
lettergrade += '+'; // 末尾是8或9的成績添加一個加號
else
lettergrade += '-'; // 末尾是3、4、5、6或是7的成績添加減號
// 縮排格式與執行過程相符,但並非程式設計師意圖。
if (grade % 10 >= 3)
if (grade % 10 > 7)
lettergrade += '+'; // 末尾是8或9的成績添加一個加號
else
lettergrade += '-'; // 末尾是3、4、5、6或是7的成績添加減號
if (grade % 10 >= 3)
{
if (grade % 10 > 7)
lettergrade += '+'; // 末尾是8或9的成績添加一個加號
}
else // 大括弧強迫else與外層if匹配
lettergrade += '-'; // 末尾是3、4、5、6或是7的成績添加減號
switch
語句switch
後面的表達式可以是整數表達式,字元表達式,也可以是 enum
型態,或可轉換為整數型態,並根據此整數回傳值決定執行動作。
每個 case
後面的常數表達式彼此間必須不同,否則會出現編譯錯誤。
switch
語句形式:
switch (expr)
{
case 常數1:
語句1;
break;
case 常數2:
語句2;
break;
.....
case 常數n:
語句n;
break;
default:
default語句;
}
switch
語句由以下4部分組成:
switch
表達式。
一組 case
條件。
與一個或一組 case
條件相關聯的語句或語句塊。
default
標籤。也可以不使用,但當所有 case
條件都不滿足時,則整個 switch 就不被執行。
若某個 case
條件匹配成功,將從該條件開始往後依序執行所有 case
分支。除非程式顯示中斷該過程,否則直到 switch
的結尾才會停止。
若要避免執行後續 case
分支,可在下一個
case
標籤之前加上 break
語句。
一般不要省略 case
分支最後的 break
語句。若不寫,請加上註釋說明之。
unsigned vowelCnt = 0;
switch (ch)
{
// 出現a、e、i、o、u中的任一個都會將vowelCnt的值加1。
case 'a'":
case 'e':
case 'i':
case 'o':
case 'u':
++vowelCnt;
break;
}
goto
語句又稱『無條件轉向語句』。
也被視為『跳轉語句』。
用來跳轉到程式某個位置進行執行。
語法:
goto 語句標號;
_
三種字元組成,且第一個字元必須為字母或 _
,另外不可為系統保留字)。與 if
語句一起構成循環結構。
從循環體內部跳轉到循環體外部(但會破壞結構化程式設計原則,建議不使用)。
在大多數情況下,可以使用其他循環語句來代替 goto
語句。
goto
語句無法用來跨函數跳轉。
int i = 1, sum = 0;
loop: // 語句標號。
if (i <= 100)
{
sum = sum + i;
i++;
goto loop;
}
printf("1 + 2 + 3 + ... + 100 和值為 %d。\n", sum);
loop:
為語句標號。後面接:
。
當程式執行到 goto loop;
語句時可以直接跳轉至
loop
標號所在行並重新繼續往下執行,如此反覆。
void func1()
{
lbl1:
int k;
k = 1;
goto lbl1;
}
void func2()
{
lbl2:
int a;
a = 1;
}
func1
函數中的 goto lbl2;
修改為
goto lbl2;
,這樣做會發生語法錯誤:因為 lbl2 位於 func2
函數內,goto 無法跨函數跳轉。while
語句語法:
while (表達式) 欲執行的語句;
先判斷表達式的值,若表達式值為真(非 0),則執行
欲執行的語句(循環體)
,然後再次循環回去,重新判斷表達式值,再決定是否再次欲執行的語句(循環體)
部分,如此反覆。
若要執行多條語句,則使用 {}
括起來。
int i = 1, sum = 0;
while (i <= 100)
{
sum = sum + i;
i++;
}
printf("1 + 2 + 3 + ... + 100 和值為 %d。\n", sum);
do … while
語句語法:
do 要執行的語句 while (表達式);
要執行的語句
,然後判斷表達式
的值,若表達式之值為
true
(非0)時,繼續執行循環體語句,然後繼續判斷表達式
的值,如此反覆,直到表達式的值為
false
(0),跳出整個do … while
語句,接著繼續往後面語句執行。至少會執行一次循環,然後才會進行判斷表達式
是否為
true
。
int i = 1, sum = 0;
do{
sum = sum + i;
++i;
} while (i <= 100);
printf("1 + 2 + 3 + ... + 100 和值為 %d。\n", sum);
do … while
語句使用情境較少,因為大多數情況下,do … while
語句可以被
while
語句來替代。
for
語句語法:for (表達式1; 表達式2; 表達式3) 內嵌語句;
for 語句執行步驟:
表達式1
求值。
表達式2
求值。
若表達式2
之值為 true
(非0),則執行 for
語句中的內嵌語句
,接著執行表達式3
,反覆循環步驟2,直到表達式2的值為
false
(0),則循環結束,跳到整個 for
語句後面的語句來執行。
表達式1
只會被執行一次。
循環執行幾次,表達式3
就執行幾次。
int i, sum = 0;
for (i = 1; i <= 100; ++i)
{
sum = sum + i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。
表達式1
可以省略,但其後的分號(;
)不能省略。
int i, sum = 0;
i = 1;
for ( ; i <= 100; ++i)
{
sum = sum + i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。
表達式2
可以省略,也就是不加入循環結束的條件,但分號(;
)仍然不能省略。
int i, sum = 0;
for (i = 1; ; ++i)
{
sum = sum + i;
if (i >= 100)
break;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。
表達式3
可以省略,但必須保證循環能正常結束,否則循環會無止盡執行。
int i, sum;
for (sum = 0, i = 1; i <= 100; )
{
sum = sum + i;
++i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。
三個表達式都省略:不設定初值,不判斷條件,循環變數不變動。如下所示:
while (1)
{
}
for(;;)
{
}
int i = 1, sum = 0;
for(;;)
{
sum = sum +i;
if (i >= 100)
break;
++i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。
表達式1
可以用來設定循環變數的初值,也可以設定與循環變數無關的其他表達式。
int i = 1, sum;
for (sum = 0; i <= 100; ++i)
{
sum = sum + i;
}
std::cout << "sum = " << sum << std::endl; // sum = 5050。
int k = 0;
for (size_t i = 2; i <= 9; i++)
{
for (size_t j = 1; j <= 9; j++)
{
k = i * j;
std::cout << i << " * " << j << " = " << k << std::endl;
}
std::cout << std::endl;
}
break
語句break
語句會中斷離它最近的
while
、do .. while
、for
或
switch
語句,並從這些語句後的第一條語句開始繼續執行。
break
語句只能出現在迭代語句或 switch
語句的內部。
break
語句通常與一個 if
語句配合使用。
for (size_t i = 0; i < 5; ++i)
{
if (i == 2)
break;
std::cout << i << std::endl;
}
int i, sum = 0;
for (i = 0; i < 100; i++)
{
sum = sum + i;
if (sum >= 4000)
break;
}
std::cout << "sum = " << sum << std::endl;
std::cout << "i = " << i << std::endl;
int i, j, k;
for (i = 1; i < 9; i++)
{
for (j = 1; j < i; j++)
{
k = i * j;
std::cout << i << " * " << j << " = " << k << std::endl;
break;html
}
printf("\n");
break;
}
std::cout << "流程結束!" << std::endl;
continue
語句continue
語句終止最近循環中當下的迭代並立即開始下一次的迭代。
continue
語句只能用在 for
、
while
與 do … while
循環的內部,或是嵌套在此類循環裡的語句或語句塊內部。
與 break
類似,當出現在嵌套循環中的
continue
語句僅作用在離它最近的循環。
與 break
不同,只有當 switch
嵌套在循環內,才能在 switch
內使用 continue
語句。
int i;
for (i = 1; i <= 100; ++i)
{
if (i % 3 == 0)
continue;
std::cout << i << std::endl;
}
for
迴圈語句分號:
#include <iostream>
int main() {
double x = 2.0;
for (int i = 0; i < 5; i++); // 分號不應該出現在這個位置
{
x *= 2.0;
}
std::cout << "x = " << x << std::endl;
return 0;
}
在for
迴圈結束的地方有一個分號;
,這使得
for
迴圈的主體變成了一個空語句,這意味著迴圈內實際上什麼都沒做。
for
迴圈後面的大括號內的語句是一個獨立的語句塊,它只執行一次,不會被
for
迴圈重複執行。
if
語句比較兩個浮點數:
在C++中,整數的相等性比較相對簡單,但對於雙精度浮點數進行相等性比較時需要考慮浮點數運算中的四捨五入誤差。下面展示了如何比較兩個整數和如何正確地比較兩個雙精度浮點數。
整數比較
假設我們有兩個整數變數i
和j
,我們希望將另一個整數變數k
設為0,如果這兩個變數的值相等,那麼我們可以使用以下程式碼:
int i = 5;
int j = 5;
int k;
if (i == j)
{
k = 0;
}
else
{
k = 1;
}
i
和j
相等,則k
被設為0;否則,k
被設為1。浮點數比較
對於雙精度浮點數變數p
和q
,由於浮點數運算可能會產生四捨五入誤差,直接比較兩個浮點數是否相等可能會得不到預期的結果。相反,我們應該檢查這兩個數的差值是否小於某個非常小的數值(如epsilon
)。
#include <iostream>
#include <cmath> // 包含cmath頭文件以使用fabs函數
using namespace std;
int main()
{
double p = 5.0;
double q = 5.000000000000186;
int k;
double epsilon = 1e-9; // 定義一個非常小的數值
if (fabs(p - q) < epsilon)
{
k = 0;
}
else
{
k = 1;
}
cout << "k = " << k << endl;
return 0;
}
epsilon
來比較它們的差值是否足夠小,以判斷它們是否相等。結合律 | 運算子 | 功能 | 語法 |
---|---|---|---|
右 | ! |
邏輯 非 | !expr |
左 左 左 左 |
|
(關係運算子) 小於 小於等於 大於 大於等於 |
|
左 左 |
|
相等 不相等 |
|
左 | && |
邏輯且 | expr && expr |
左 | || |
邏輯 或 | expr || expr |
『邏輯且』運算子(&&
):當左右兩側運算元都為真時,結果為真。
『邏輯或』運算子(||
):只要當兩側運算元中一個為真時,結果為真。
&&
與
||
運算子都是由左側運算元先運算,再求右側運算元之值。
短路求值(short-circuit evalution):只有當左側運算元無法確認表達式最後結果時,才會對右側運算元進行求值。
&&
:左側為真(true
),才會對右側運算元求值。
||
:左側為假(false
),才會對右側運算元求值。
『邏輯非』(!
)運算子會將運算元之值取相反後並將其回傳之。
關係運算子:比較兩側運算元大小關係並回傳布林值。滿足『左結合律』。注意下例:
if (i < j < k) // 注意:拿 i < j 的布林值結果與k比較
{ // 若k大於1則一定為真
}
if (i < j && j < k) // 應該寫成這樣才正確。
{
}
上述問題在於布林值的比較操作。 在 C++ 中使用連續比較運算子(如
i < j < k
)時,表達式的行為可能不是你所期望的。這是因為
C++
的比較運算子是從左到右依次求值的,而布林值(true
或 false
)會被隱式轉換為整數
1
或 0
。
例如,表達式 i < j < k
將會被解析為 (i < j) < k
,這樣
i < j
的結果是一個布林值(true
或
false
),然後這個布林值會被轉換為整數
1
或 0
,再與
k
進行比較。這可能會導致意想不到的結果。
正確作法應為使用邏輯運算子
&&
來明確地進行兩次比較:
相等性測試與布林字面值
if (val) {/*......*/ } // 若val是任意非0值,則條件為真
if (!val) {/*.....*/} // 若val為0,則條件為真。
if (val == true) {/*.....*/} // 只有當val等於1時,條件才為真。
若 val 不是布林值,則進行比較前會先將 true
轉換為
val 的型態,意指:
if (val == 1) {/*.....*/}
進行比較運算時,除非比較的對象為布林值,建議不要使用布林字面值
true
或 false
作為運算元。
賦值運算子(=
)左側的運算元一定是一個可被修改的『左值(變數)』,並將『等號右側』的值賦值給『等號左側』的變數。
可理解成:給變數一個值,或是更改變數內容為某一個值。
char a;
:此為變數宣告(未初始化)。系統會將變數
a 分配一個 byte的記憶體空間(未定義值)。
char a = 90;
:定義時初始化(非賦值語句)。系統會給變數
a 分配記憶體空間,接著在這個內存上放入值 90。
int i = 0, j = 0, k = 0 // 初始化而非賦值。
const int ci = i; // 初始化而非賦值。
下列賦值語句皆為錯誤:
1024 = k; // 錯誤:字面值為右值。
i + k = k; // 錯誤:算術表達式為右值。
ci = k; // 錯誤:ci為 const 常數的左值。
賦值表達式的結果是『左側運算元』,且其為『左值』,而結果的型態就是左側運算元的型態。
若賦值運算子的左右兩側運算元型態不同,則右側運算元將轉換成左側運算元的型態。
k = 0; // 型態為int,值為0。
k = 3.14; // 型態為int,值為3。
C++ 11
標準:允許使用大括弧({}
)的『初始化列表』作為賦值運算子右側的運算元:
k = {3.14}; // 錯誤:發生型態窄化轉換。
std::vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
賦值運算子滿足『右結合律』,此與二元運算子不同。
int ival, jval;
ival = jval = 0; // 正確:都被賦值為0。
// ival = (jval = 0); // 滿足右結合律。
int ival, *pval;
ival = pval = 0; // 無法將指標值(int*)賦值給int
賦值運算子(=
)優先級較低。
int i;
while ( (i = get_value() ) != 42)
{
// 其他的處理
}
上述程式碼 while
的條件內若不加上
()
,則比較運算子 !=
的運算元將是
get_value
函數回傳值與
42
,比較結果顯然不是我們所預期的答案。
故在條件語句中,賦值部分應該加上()
。
複合賦值運算子。
a = a operator b
(a += 1
等價
a = a + 1
)
+=
-=
*=
/=
%=
優先級順序:賦值運算子 < 關係運算子 < 算術運算子
。
共兩種:
遞增運算子(++
):為物件加一,又分『前置』,如
++i
與『後置』,如 i++
。
遞減運算子(--
):為物件減一,又分『前置』,如
--i
與『後置』,如 i--
。
前置遞增(遞減)運算子:對『左值運算元』加一(減一),再將『改變之後的物件』作為執行結果(作為回傳值)。
後置遞增(遞減)運算子:也是對『左值運算元』加一(減一),但執行結果是運算元被『改變之前的物件』版本的拷貝。
int i = 0, j;
j = ++i;
std::cout << "j = ++i" << std::endl;
std::cout << "i = "<< i << std::endl;
std::cout << "j = "<< j << std::endl;
int i = 0, j;
j = i++;
std::cout << "ex: i++" << std::endl;
std::cout << "j = i++" << std::endl;
std::cout << "i = "<< i << std::endl;
std::cout << "j = "<< j << std::endl;
前置版本:將物件本身作為『左值』回傳。
後置版本:將物件的原始值的副本(拷貝)作為『右值』回傳。
注意:除非必要,建議『不要』使用遞增遞減運算子的『後置版本』,原因如下:
前置版本避免了不必要的工作:它將運算元加一(減一)後,直接回傳改變後的運算元。
後置版本需要將原始值進行儲存,以便於之後回傳未修改的內容。
若不需要修改前的原始值,則建議使用前置版本。
範例——語句中混用『解參考運算子』與『遞增運算子』:
auto pbeg = v.begin();
// 輸出元素直到遇到第一個負值為止
while (pbeg != v.end() && *beg >= 0)
{
std::cout << *pbeg++ << std::endl; // 輸出當前值,並將pbeg向前移動一個元素
}
// std::cout << *iter++ << std::endl; // 更簡潔~~
// 上述語句可以取代下列兩行語句:
// std::cout << *iter << std::endl;
// ++iter;
*pbeg++
等於
*(pbeg++)
:因為『遞增運算子』的優先級高於『解參考運算子』,故
()
可以省略。
此時解參考運算子的運算元是『pbeg
』未增加之前的值。
此語句輸出 pbeg
開始時指向的那個元素,並將指標向前移動一個位置。
注意:大部分運算子沒有規定運算元的求值順序。故當一條子表達式改變運算元的值,而存在另外一條使用該運算元值的子表達式時,求值順序就非常重要了。
for (auto it = s.begin(); it != s.end() && !issapce(*it); ++i)
{
*it = toupper(*it); // 將當前字元改為大寫形式。
}
while (beg != s.end() && !isspace(*beg))
{
*beg = toupper(*beg++); // 錯誤:該賦值語句無法定義
}
上述範例將產生未定義的原因為:
賦值運算子左右兩側的運算元都用到
beg
,且右側的運算元還改變了 beg
的值,故該語句是未定義的。
編譯器可能按照下列任意一種方式處理該表達式:
*beg = toupper(*beg); // 若先求左側的值。
*(beg + 1) = toupper(*beg); // 若先求右側的值。
前置遞增遞減運算子(++i
與
–-i
)為『左值表達式』(回傳左值):
++i
:直接給 i 變數加 1,然後回傳 i 本身。因為 i
為變數,所以可以被賦值,故為左值表達式。
int i = 5;
(++i) = 20; // i被賦值為20。
後置遞增遞減運算子(i++
與
i--
)為『右值表達式』(回傳右值):
i++
:先產生一個『臨時變數』來保存 i
的值用於回傳,接著再對 i 加
1,之後系統在釋放上述的『臨時變數』。被釋放掉的臨時變數將無法被賦值。故為『右值表達式』。
int i = 5;
// (i++) = 20; // 錯誤:表達式必須是可被修改的左值。
int i = 1;
int &&r1 = i++; // 正確:右值參考可以綁定右值,但與r1與i沒有關係。
// int &r2 = i++; // 錯誤:左值參考無法綁定右值表達式。
int &r3 = ++i; // 正確:左值參考可以綁定左值。
// int &&r4 = ++i; // 錯誤:右值參考無法綁定左值。
std::cout << "i = " << i << std::endl;
std::cout << "r1 = " << r1 << std::endl;
std::cout << "r3 = " << r3 << std::endl;
補充說明:
&&r1
綁定右值,但 r1
本身為左值。
所有變數都為左值。
任何函數的形式參數都為左值,如
void f(int &&w)
,形式參數 w
的型態為右值參考(意指其可以綁定右值),但 w
本身為左值。
點運算子(.
):可取得物件的成員(member)。
箭頭運算子(->
):等價於
(*ptr).mem
。
std::string s1 = "a string", *p = &s1;
auto n = s1.size(); // 執行string物件s1的size成員。
n = (*p).size(); // 執行p所指向的物件的size成員。
n = p->size(); // 等價於 (*p).size()。
因為『解參考運算子』的優先級「低」於『點運算子』,故執行解參考運算的子表達式必須加上
()
。
// *p.size(); // 錯誤:p為一個指標,它並沒有名為size的成員。
箭頭運算子作用於一個指標型態的運算元,其結果為『左值』。
點運算子有兩種情況:
若成員所屬的物件為左值,則結果為左值。
若成員所屬的物件為右值,則結果為右值。
條件運算子(? :
):允許將簡單的 if-else
的邏輯嵌入到單個表達式中:
語法:cond ? expr1 : expr2;
其中,cond
是判斷條件的表達式,而 expr1
與 expr2
是兩個型態相同或可能轉換為某個型態的表達式。
先執行 cond
的值,若條件為真(true
)則對 expr1
求值並回傳該值,否則對 expr2
求值並回傳結果。
max = (a > b)? a:b;
逗點運算子( ,
comma
operator)是優先級『最低』的運算子。
逗點運算子含兩個運算元,按照『由左至右』的順序依序求值:
表達式1, 表達式2, 表達式3, ……., 表達式N
逗點運算子先對左側表達式求值,接著將求值結果丟棄。其真正的回傳值為『最右側表達式』之值(表達式N
)。
std::vector<int>::size_type cnt = ivec.size();
for(vector<int>::size_type ix = 0; ix != ivec.size(); ++ix, --cnt)
ivec[ix] = cnt;
上述循環 for
語句中的表達式進行:遞增
ix
,遞減 cnt
,每次循環迭代 ix
與
cnt
都會改變。
只要 ix
滿足條件,則將當前元素設定為
cnt
當下之值。
int a;
a = (4, 5); // a = 5,因為逗點運子優先級太低,故必須加()。
a = (3 + 5, 6 + 8); // a = 14。
a = 3 * 5, a * 4; // 表達式值為60。因為先計算a = 3*5,再計算a*4。故a的值為15,整個逗點表達式為60。
int x, a;
x = (a = 3, 6*3); // a = 3, x = 18。
x = a = 3, 6*a; // a = 3, x = 3,逗點表達式的結果為18,但並未被使用。
int result = (x = a = 3, 6 * a); // result = 18。
使用強制型態轉換時,則會抑制編譯器的報錯行為,故強制型態轉換一般不建議使用。
建議使用 C++ 風格。
相較於之前討論的『隱式型態轉換』,亦存在『顯式型態轉換』(又稱『強制型態轉換』)。如:
// int k = 5 % 3.2; // 錯誤: % 運算子兩側必須為整數型態。
int k = 5 % (int)3.2;
// 也可以寫成下列形式
int k2 = 5 % int(3.2);
()
:沒有形態的檢查,型態轉換的對錯由程式設計師負責。C++ 提供更安全的『強制型態轉換』:
static_cast<>
dynamic_cast<>
const_cast<>
reinterpret_cast<>
語法:強制型態轉換名稱<type>(expression);
static_cast<>
:
『靜態轉換』,可視為一般轉換,與 C 語言的強制型態類似。
編譯期就會進行型態轉換的檢查。
double f = 100.123f;
int i = (int)f; // C語言風格。
int i2 = static_cast<int>(f); // 100, C++語言風格。
子類別轉換為父類別時,也可以使用
static_cast
。
class A // 父類別
{
};
class B:public A // 子類別
{
};
int main()
{
B b;
A a = static_cast<A>(b); // 允許將子類別轉換為父類別。反之,則錯誤。
return 0;
}
void*
指標與其他型態指標之間的轉換:
void*
為『無型態指標』,也就是可以指向任何型態的指標。int i = 10;
int *p = &i;
void *q = static_cast<void*>(p);
int *db = static_cast<int*>(q);
不可用於指標型態之間的轉換,如 int*
轉
double*
等。
double f = 100.0;
double *pf = &f;
// int *i = static_cast<int*>(pf); // 錯誤。
// float *fd = static_cast<float*>(pf); // 錯誤。
dynamic_cast<>
:
顧名思義,該轉換用於『執行期』進行型態檢查。
主要用於『父類別轉換為子類別』。
它主要用於將父類別指標或參考轉換為子類別型態。
若轉換失敗,dynamic_cast<>
則回傳
nullptr
(如果轉換的是指標)或拋出
std::bad_cast
異常(如果轉換的是參考)。
在類型安全性方面提供了保證,但相較於其他類型的轉換有較高的成本。
const_cast<>
:
移除指標變數或參考的
const
的屬性。故功能較為單純。
編譯期進行此型態轉換檢查。
若原來變數為 const
,當強制使用
const_cast
移除 const
屬性並修改其值,此為未定義行為,請避免之。
const int ai = 90;
//int ai2 = const_cast<int>(ai); // 錯誤:因ai不是指標,也不是參考,故無法轉換。
const int *pai = &ai;
int *pai2 = const_cast<int*>(pai); // 正確。
*pai2 = 120; // 建議不要這樣做!
std::cout << ai << std::endl;
std::cout << *pai << std::endl;
reinterpret_cast
:
可用來處理兩個型態無關連型態轉換。
編譯期進行此型態轉換檢查。
這種轉換是非常低層次的,並且通常與底層機器的內存表示相關,因此被認為是危險的。
因編譯器不會提出任何警告與錯誤訊息,故具危險性。建議避免使用。
轉換指標類型:reinterpret_cast
可用於將一個指標類型轉換為另一個完全不相關的指標類型。
轉換為整數類型:reinterpret_cast
可用於將指標轉換為整數類型,這在某些底層編程中可能是必要的。