C++語言定義的標準轉換
標準轉換
C++ 語言定義其基礎類型之間的轉換。 它還定義指針、引用和指向成員的指針派生類型的轉換。 這些轉換稱為“標準轉換。
1. 整型提升
整數類型的對象可以轉換為另一個更寬的整數類型(即,可表示更大的一組值的類型)。 這種擴展類型的轉換稱為“整型提升”。 利用整型提升,您可以在可使用其他整數類型的任何位置將以下項用於表達式:
-
char
和short int
類型的對象、文本和常量 -
枚舉類型
-
int
位域 -
枚舉器
C++ 提升是“值保留”。 即,提升後的值一定與提升前的值相同。 在值保留提升中,如果 char
可以表示原始類型的完整範圍,較短的整數類型的對象(如 int
int
類型。 如果 int
無法表示完整範圍的值,該對象將提升到 unsigned int
類型。 盡管此策略與 ANSI C 中使用的相同,但值保留轉換不保留對象的“符號”。
值保留提升和保留符號的提升通常會生成相同的結果。 但是,如果提升的對象是以下項之一,它們可能生成不同的結果:
-
/、
%
、/=
、%=
、<、<=、> 或 >= 的操作數這些運算符依賴於用於確定結果的符號。 因此,當值保留和符號保留提升應用於這些操作數時,它們將生成不同的結果。
-
>> 或 >>= 的左操作數
當執行移位運算時,這些運算符會區別對待有符號的數量和無符號的數量。 對於有符號的數量,將數量右移會導致符號位傳播到空出的位位置。 對於無符號的數量,空出的位位置將由零填充。
-
重載函數的參數,或重載運算符的操作數(取決於該操作數的用於參數匹配的類型的符號)。
2. 整型轉換
整型轉換在整型之間執行。 整型包括 char
、int
和 long(以及這些類型的 short、signed 和 unsigned
版本)
(1)有符號轉換為無符號
有符號整數類型的對象可以轉換為對應的無符號類型。 當這些轉換發生時,實際位模式不會更改;但是,數據的解釋會更改。
// conve__pluslang_Converting_Signed_to_Unsigned.cpp // compile with: /EHsc #include <iostream> usingnamespace std; int main() { short i = -3; unsigned short u; cout << (u = i) << "\n"; } // Output: 65533
在前面的示例中,signed short
i
被定義和初始化為負數。 表達式 (u = i)
導致 i
在為 賦值前轉換為 u
unsigned short。
(2)無符號轉換為有符號
無符號整數類型的對象可以轉換為對應的有符號類型。 但是,如果無符號對象的值在有符號類型表示的範圍之外,則此類轉換可能導致數據錯誤解釋,如以下示例所示:
// conve__pluslang_Converting_Unsigned_to_Signed.cpp // compile with: /EHsc #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
中正確表示,因此數據被錯誤解釋。
3. 浮點轉換
浮動類型的對象可以安全地轉換為更精確的浮點類型,也就是說,轉換不會導致基數丟失。 例如,從 float 到 double 或從 double 到 long double
的轉換是安全的,並且值保持不變。
如果浮點類型的對象位於低精度類型可表示的範圍中,則還可轉換為該類型。 (有關浮點類型的範圍,請參閱浮點限制。) 如果原始值不能精確地表示,則可將其轉換為下一更高或更低的可表示值。 如果此類值不存在,則結果是不確定的。
4. 整型和浮點型之間的轉換(有截斷誤差)
某些表達式可能導致浮點型的對象轉換為整型,反之亦然。 當整型對象轉換為浮點型且無法正確表示原始值時,結果要麽是下一個較大的可表示值,要麽是下一個較小的可表示值。
當浮點型的對象轉換為整型時,小數部分將被截斷。 轉換過程中不會進行舍入。 截斷意味著,數字 1.3 將轉換為 1,–1.3 將轉換為 –1。
5、 算術轉換
很多二元運算符(在帶二元運算符的表達式中有討論)會導致操作數轉換並以相同的方式產生結果。 這些運算符導致轉換的方式稱為“常用算術轉換”。 不同本機類型的操作數的算術轉換按下表所示的方式執行。 Typedef 類型的行為方式基於其基礎本機類型。
其中一個操作數是 long double 類型。 | 另一個操作數將轉換為 long double 類型。 |
未滿足上述條件,並且其中一個操作數是 double 類型。 | 另一個操作數將轉換為 double 類型。 |
未滿足上述條件,並且其中一個操作數是 float 類型。 | 另一個操作數將轉換為 float 類型。 |
未滿足上述條件(沒有任何一個操作數屬於浮動類型)。 | 整型提升按以下方式對操作數執行: - 如果其中一個操作數是 unsigned long 類型,則另一個操作數將轉換為 unsigned long 類型。- 如果未滿足上述條件,並且其中一個操作數是 long 類型,另一個操作數是 unsigned int 類型,則兩個操作數都將轉換為 unsigned long 類型。- 如果未滿足上述兩個條件,並且其中一個操作數是 long 類型,則另一個操作數將轉換為 long 類型。 - 如果未滿足上述三個條件,並且其中一個操作數是 unsigned int 類型,則另一個操作數將轉換為 unsigned int 類型。- 如果上述條件均未滿足,則兩個操作數都將轉換為 int 類型。 |
6. 指針轉換
在賦值、初始化、比較和其他表達式中,可以轉換指針。
(1)指向類的指針
在兩種情況下,指向類的指針可轉換為指向基類的指針。
第一種情況是指定的基類可訪問且轉換是明確的。
基類是否可訪問取決於派生中使用的繼承的類型。 考慮下圖中闡釋的繼承。
下表顯示針對該圖闡釋的情況的基類可訪問性。
由表中可知:
- 直接從派生類指針轉換成父類指針,只能有時Public繼承,才能合法轉換為父類指針;
- 在子類內部, 直接將 (A*)this,即不管繼承方式,都可以合法轉換使用,且根究A的成員訪問屬性,以及繼承方式訪問特定函數。
- 在子子類內部, 因為B private 繼承了A, 則限制了再子子C中的訪問,因此該情況無效。
函數的類型 | 派生 | 從 B* 到 A* 的轉換是否合法? |
---|---|---|
外部(非類範圍)函數 | Private | No |
Protected | No | |
Public | 是 | |
B 成員函數(在 B 範圍內) | Private | 是 |
Protected | 是 | |
Public | 是 | |
C 成員函數(在 C 範圍內) | Private | No |
Protected | 是 | |
Public | 是 |
第二種情況是,在您使用顯式類型轉換時,指向類的指針可轉換為指向基類的指針。只能訪問由基類的屬性。
此類轉換的結果是指向完全由基類描述的對象部分(即“子對象”)的指針。
以下代碼定義了兩個類(即 A
和 B
),其中 B
派生自 A
。 (有關繼承的詳細信息,請參閱派生類。) 然後定義 bObject
、類型 B
的對象和兩個指向該對象的指針(pA
和 pB
)。
// conve__pluslang_Pointers_to_Classes.cpp // 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
中定義的對象的那些特性(成員函數和數據)。
7. 指向函數的指針
如果類型 void * 足以保留指向函數的指針,則該指針可以轉換為 void * 類型。指向函數的指針可以轉化成為void*。
8 指向 void 的指針
指向 void
類型的指針可以轉換為指向其他任何類型的指針,但僅適合於顯式類型轉換(與在 C 中的情況不同)。
指向任何類型的指針可以隱式轉換為指向類型 void
的指針。指向類型的不完整對象的指針可以轉換為指向 void
(隱式)和 back(顯式)的指針。 此類轉換的結果與原始指針的值相等。 對象被視為是不完整的(如果已聲明對象),但未提供足夠多的可用信息,無法確定其大小或基類。
指向不是 const 或 volatile
的任何對象的指針可以隱式轉換為 void * 類型的指針。
9. 固定和可變指針
C++ 將不會應用從 const 或 volatile
類型到不是 const 或 volatile
類型的標準轉換。 但是,任何類型的轉換都可以用顯式類型強制轉換指定(包括不安全的轉換)。
指向成員的 C++ 指針(指向靜態成員的指針除外)與常規指針不同,二者具有不同的標準轉換。 指向靜態成員的指針是普通指針,且與普通指針具有相同的轉換。
10 null 指針轉換
計算結果為零的整數常量表達式
或到某個指針類型的此類表達式強制轉換,將轉換為稱為“null 指針”的指針。 此指針與指向任何有效對象或函數的指針比較的結果肯定不會相等(指向基對象的指針除外,此類指針可以有相同的偏移量並且仍指向不同的對象)。
在 C++11 中, nullptr 類型應優先於 C 樣式 null 指針
11 指針表達式轉換
帶數組類型的所有表達式都可以轉換為同一類型的指針。 轉換的結果是指向第一個數組元素的指針。 下面的示例演示了這樣的轉換:
char szPath[_MAX_PATH]; // Array of type char. char *pszPath = szPath; // Equals &szPath[0].
12 引用轉換
對類的引用可在以下情況下轉換為對基類的引用:
-
指定的基類是可訪問的(如指向類的指針中定義的那樣)。
-
轉換是明確的。 (有關不明確的基類引用的詳細信息,請參閱多個基類。)
轉換的結果為指向表示基類的子對象的指針。
13 指向成員的指針
指向可在賦值、初始化、比較和其他語句中轉換的類成員的指針。
14 指向基類成員的指針
當滿足以下條件時,指向基類的成員的指針可以轉換為指向派生自基類的類的成員的指針:
-
從指向派生類的指針到基類指針的反向轉換可以訪問。
-
派生類並非以虛擬方式從基類繼承。【因為虛擬方式繼承,函數屬於子類,而非父類】
當左操作數是指向成員的指針時,右操作數必須是 pointer-to-member 類型或計算結果為 0 的常量表達式。 此賦值僅在以下情況下有效:
-
右操作數是指向與左操作數相同的類的成員的指針。
-
左操作數是指向以公共但不明確的方式派生自右操作數的類的成員的指針。
15. 整數常量轉換
計算結果為零的整數常量表達式將轉換為名為“null 指針”的指針。 此指針與指向任何有效對象或函數的指針比較的結果肯定不會相等(指向基對象的指針除外,此類指針可以有相同的偏移量並且仍指向不同的對象)。
以下代碼演示了指向類 i
中的成員 A
的指針的定義。 指針 pai
將初始化為 0,因此是 null 指針。
// conve__pluslang_Integral_Constant_Expressions.cpp class A { public: int i; }; int A::*pai = 0; int main() { }
因為:
endl.
C++語言定義的標準轉換