C++ 語言定義其基本類型之間的轉換。 同時定義指標、參考及成員指標衍生類型的轉換。 這些轉換稱為 標準轉換。
本節將討論下列標準轉換:
整數提升
整數轉換
浮動轉換
浮動和整數轉換
算術轉換
指標轉換
參考轉換
成員指標轉換
下列程式碼會引發轉換 (本範例是整數提升):
long long_num1, long_num2;
int int_num;
// int_num promoted to type long prior to assignment.
long_num1 = int_num;
// int_num promoted to type long prior to multiplication.
long_num2 = int_num * long_num2;
必須產生參考類型,轉換結果才會是左值。 例如,宣告為 operator int&() 的使用者定義轉換會傳回參考,且 為 l 值。 不過,宣告為 operator int() 的轉換會傳回 物件,而且不是 l 值。
整數提升
整數型別的物件可以轉換成另一個更廣泛的整數類型,也就是可以代表較大值集合的類型。 這種擴大型別的轉換稱為 整數升階。 使用整數升階時,您可以在表達式中使用下列類型,只要有另一個整數類型可以使用:
型
char別和的物件、常值和常數short int列舉類型
int位欄位列舉值
C++促銷是「值保留」,因為促銷后的值保證與升階前的值相同。 在保留值升階中,如果char可以代表原始型別的完整範圍,則會將較短整數型別的物件(例如位字段或型int別的物件)升階為類型int。 如果 int 無法代表值的完整範圍,則會將 物件升階為 類型 unsigned int。 雖然此策略與標準 C 所使用的策略相同,但保留值轉換不會保留物件的「帶正負號」。
保留值的提升和保留正負號狀態的提升通常會產生相同的結果。 不過,如果升級的對象顯示為下列專案,它們可能會產生不同的結果:
、
/%、/=%=、<、<=、 或 的操作數>>=這些運算子需要依據正負號判斷結果。 套用至這些操作數時,保留值和簽章升級會產生不同的結果。
或的
>>左操作數>>=這些運算子會將帶正負號和不帶正負號的數量以不同的方式處理班次作業中。 對於帶正負號的數量,右移作業會將符號位傳播到空位位置,而空位位置則以零填入未帶正負號的數量。
多載函式的自變數,或多載運算子的操作數,取決於操作數類型的帶正負號來比對自變數。 如需定義多載運算子的詳細資訊,請參閱 多載運算元。
整數轉換
整數轉換是整數類型之間的轉換 。 整數型別為 char、 short (或 short int)、 int、 long、 和 long long。 這些型別可以限定為 signed 或 ,而且unsigned可以做為 的速記unsignedunsigned int。
已簽署至未簽署
帶正負號整數類型的物件可以轉換成對應的不帶正負號的類型。 發生這些轉換時,實際的位模式不會變更。 不過,數據解譯會變更。 請參考下列程式碼:
#include <iostream>
using namespace std;
int main()
{
short i = -3;
unsigned short u;
cout << (u = i) << "\n";
}
// Output: 65533
在上述範例中,會signed shorti定義 、,並將其初始化為負數。 表示式(u = i)會導致在指派i之前轉換成 unsigned shortu 。
未簽署至已簽署
不帶正負號整數類資料類型的物件可以轉換成對應的帶正負號資料類型。 不過,如果未帶正負號的值超出帶正負號類型的可表示範圍,結果就不會有正確的值,如下列範例所示:
#include <iostream>
using namespace std;
int main()
{
short i;
unsigned short u = 65533;
cout << (i = u) << "\n";
}
//Output: -3
在上述範例中, u 是 unsigned short 必須轉換成帶正負號數量以評估表達式 (i = u)的整數物件。 由於其值無法在 中 signed short正確表示,因此數據被錯誤解譯,如下所示。
浮點轉換
浮動類型的物件可以安全地轉換成更精確的浮動類型,也就是說,轉換不會遺失重要性。 例如,從 float 到 double 或 從 doublelong double 轉換是安全的,而且值不變。
如果浮點類型位於該類型所代表的範圍中,也可以轉換成較不精確的類型。 (請參閱 浮動類型範圍的浮動限制 。)如果原始值無法精確表示,則可以轉換成下一個較高或下一個較低的可表示值。 如果不存在這類值,則結果為未定義。 請考慮下列範例:
cout << (float)1E300 << endl;
依類型 float 表示的最大值是 3.402823466E38,其數位遠小於 1E300。 因此,數位會轉換成無限大,結果為 “inf”。
整數與浮點類型之間的轉換
某些運算式可能會導致浮點類型的物件轉換成整數類型,反之亦然。 當整數型別的物件轉換成浮點型別,而原始值無法完全表示時,結果會是下一個較高或下一個較低的可表示值。
當浮點數類型的 物件轉換成整數類型時,小數部分會 截斷或四捨五入為零。 1.3 之類的數位會轉換成 1,而 -1.3 會轉換成 -1。 如果截斷的值高於最高可表示值,或低於最低可表示值,則結果為未定義。
算術轉換
許多二元運算元(在表達式中 討論二元運算符)會導致操作數的轉換,並以相同方式產生結果。 這些運算子所造成的轉換稱為 一般算術轉換。 執行具有不同原生類型之操作數的算術轉換,如下表所示。 Typedef 類型是根據其基礎原生類型而運作。
類型轉換的條件
| 符合條件 | 轉換 |
|---|---|
任一操作數的類型為 long double。 |
其他操作數會轉換成 類型 long double。 |
不符合上述條件,且任一操作數的類型為 double。 |
其他操作數會轉換成 類型 double。 |
不符合上述條件,且任一操作數的類型為 float。 |
其他操作數會轉換成 類型 float。 |
| 不符合上述條件 (所有運算元都不是浮動類型)。 | 操作數會取得整數升階,如下所示: - 如果任一操作數的類型為 unsigned long,則另一個操作數會轉換成 類型 unsigned long。- 如果上述條件不符合,而且任一操作數的類型和另一個類型 longunsigned int,則這兩個操作數都會轉換成 類型unsigned long。- 如果不符合上述兩個條件,而且任一操作數為 類型 long,則另一個操作數會轉換成 類型 long。- 如果不符合上述三個條件,而且任一操作數的類型為 unsigned int,則另一個操作數會轉換成 類型 unsigned int。- 如果沒有符合上述條件,兩個操作數都會轉換成 類型 int。 |
下列程式碼說明表格中所述的這些轉換規則:
double dVal;
float fVal;
int iVal;
unsigned long ulVal;
int main() {
// iVal converted to unsigned long
// result of multiplication converted to double
dVal = iVal * ulVal;
// ulVal converted to float
// result of addition converted to double
dVal = ulVal + fVal;
}
在上述範例中的第一個陳述式會顯示兩個整數類資料類型 (iVal 和 ulVal) 的乘法。 符合的條件是,兩個操作數都不是浮點類型,而一個操作數的類型為 unsigned int。 因此,另一個操作數 iVal, 會轉換成 類型 unsigned int。 結果接著會指派給 dVal。 這裡符合的條件是一個操作數的類型 double,因此 unsigned int 乘法的結果會轉換成 類型 double。
此範例中的第二個語句會顯示和整數型別的 float 加法: fVal 和 ulVal。 變數 ulVal 會轉換成類型 float (數據表中的第三個條件)。 加法的結果會轉換成類型 double (資料表中的第二個條件),並指派給 dVal。
指標轉換
在執行指派、初始化、比較和其他運算式時,可以轉換指標。
類別的指標
在兩種情況下,類別的指標可以轉換成基底類別的指標。
第一種情況是可存取指定的基底類別,而且轉換明確。 如需模棱兩可基類參考的詳細資訊,請參閱 多個基類。
基底類別是否可存取,取決於衍生中所使用的繼承種類。 請考慮下圖所示的繼承:
此圖顯示基類 A。類別 B 會透過私人受保護的公用繼承自 A。 類別 C 會透過公用 B 繼承自 B。
說明基類輔助功能的繼承圖表
下表說明圖中所示情況的基底類別存取範圍。
| 函式類型 | 衍生 | 從B* 合法嗎 A* ? |
|---|---|---|
| 外部 (非類別範圍) 函式 | 私用 | No |
| Protected | No | |
| 公開 | Yes | |
| B 成員函式 (B 範圍中) | 私用 | Yes |
| Protected | Yes | |
| 公開 | Yes | |
| C 成員函式 (C 範圍中) | 私用 | No |
| Protected | Yes | |
| 公開 | Yes |
類別指標可以轉換成基底類別指標的第二種情況,是使用明確類型轉換 如需明確類型轉換的詳細資訊,請參閱 明確類型轉換運算符。
這類轉換的結果是子物件的指標,這是基類完全描述的物件部分。
下列程式碼會定義兩種類別 A 和 B,其中 B 衍生自 A 如需繼承的詳細資訊,請參閱 衍生類別。 然後, bObject它會定義 、型 B別 的物件,以及指向 物件的兩個指標(pA 和 pB)。
// C2039 expected
class A
{
public:
int AComponent;
int AMemberFunc();
};
class B : public A
{
public:
int BComponent;
int BMemberFunc();
};
int main()
{
B bObject;
A *pA = &bObject;
B *pB = &bObject;
pA->AMemberFunc(); // OK in class A
pB->AMemberFunc(); // OK: inherited from class A
pA->BMemberFunc(); // Error: not in class A
}
pA指標的類型為 A *,這可以解譯為「類型A物件的指標」。(例如 bObject 和BComponent) 的成員對 型別BMemberFunc而言是唯一的B,因此無法透過pA存取。
pA 指標只允許存取 A 類別中定義的那些物件特性 (成員函式和資料)。
函式的指標
如果型void *別夠大,則可以將函式的指標轉換成 類型void *,以保存該指標。
void 的指標
型 void 別的指標可以轉換成任何其他類型的指標,但只能使用明確的型別轉換(不同於 C)。 任何型別的指標可以隱含地轉換成 型別 void的指標。 型別不完整物件的指標可以轉換成 void 指標(隱含地)和背面(明確)。 這類轉換的結果等於原始指標的值。 如果物件已宣告,則視為不完整,但沒有足夠的資訊可判斷其大小或基類。
任何不是 const 或 volatile 可以隱含轉換成 型 void *別指標之物件的指標。
const 和 volatile 指標
C++不提供從 const 或 volatile 型別到不是 const 或 volatile的類型的標準轉換。 不過,使用明確類型轉型 (包括不安全的轉換) 即可指定任何類型的轉換。
注意
除了靜態成員的指標以外,C++指標與一般指標不同,而且沒有相同的標準轉換。 靜態成員的指標是一般指標,具有與一般指標相同的轉換。
null 指標轉換
評估為零的整數常數表達式,或轉換成指標類型的運算式,會轉換成稱為 null 指標的 指標。 此指標一律會將不相等的指標與任何有效的物件或函式進行比較。 例外狀況是基底物件的指標,其可以有相同的位移,而且仍然指向不同的物件。
在 C++11 中, Nullptr 類型應該優先於 C 樣式的 Null 指標。
指標運算式轉換
具有陣列類型的任何運算式都可以轉換成相同類型的指標。 轉換的結果是第一個陣列元素的指標。 下列範例將示範這類轉換:
char szPath[_MAX_PATH]; // Array of type char.
char *pszPath = szPath; // Equals &szPath[0].
導致函式傳回特殊類型的運算式會轉換成傳回該類型之函式的指標,但下列情形例外:
表達式會當做運算子位址的操作數使用(&)。
運算式做為函式呼叫運算子的運算元。
參考轉換
在這些情況下,類別的參考可以轉換成基類的參考:
指定的基類是可存取的。
轉換不可以模稜兩可。 如需模棱兩可基類參考的詳細資訊,請參閱 多個基類。
轉換的結果是表示基底類別子物件的指標。
成員的指標
在執行指派、初始化、比較和其他運算式時,可以轉換類別成員的指標。 本節將描述下列成員指標的轉換:
基底類別成員的指標
當下列條件符合時,就可以將基底類別成員的指標轉換為從其衍生之類別成員的指標:
反向轉換 (即從衍生類別的指標轉換為基底類別指標) 是可以存取的。
衍生類別不會幾乎繼承自基類。
當左運算元為成員的指標時,右運算元必須是成員指標類型,或者必須是判斷值為 0 的常數運算式。 這項指派只在下列情況下有效:
右運算元為與左運算元相同類別的成員指標。
左運算元是一種公開且明確地從右運算元類別所衍生的類別成員指標。
成員轉換的 null 指標
評估為零的整數常數表達式會轉換成 Null 指標。 此指標一律會將不相等的指標與任何有效的物件或函式進行比較。 例外狀況是基底物件的指標,其可以有相同的位移,而且仍然指向不同的物件。
下列程式碼說明在 i 類別中針對成員 A 所定義的指標。 指標 (即 pai) 已初始化為 0,為 Null 指標。
class A
{
public:
int i;
};
int A::*pai = 0;
int main()
{
}