C++ 類型系統
類型 的概念 在 C++ 中很重要。 每個變數、函式引數和函式傳回值都必須有類型才能編譯。 此外,所有運算式(包括常值)都會在評估之前,由編譯器隱含地指定類型。 某些類型的範例包括內建類型,例如 int
儲存整數值、 double
儲存浮點值,或標準程式庫類型,例如類別 std::basic_string
來儲存文字。 您可以藉由定義 class
或 struct
來建立自己的類型。 此類型會指定為變數 (或運算式結果) 配置的記憶體數量。 此類型也會指定可能儲存的值種類、編譯器如何解譯這些值中的位模式,以及您可以對其執行的作業。 本文包含 C++ 類型系統主要功能的簡略概觀。
辭彙
純量類型 :保留所定義範圍之單一值的型別。 純量包括算術類型(整數或浮點值)、列舉型別成員、指標類型、指標對成員型別和 std::nullptr_t
。 基本類型通常是純量型別。
複合類型 :不是純量型別的類型。 複合類型包括陣列類型、函式類型、類別(或結構)類型、等位型別、列舉、參考,以及非靜態類別成員的指標。
變數 :資料數量的符號名稱。 此名稱可用來存取它所參考之整個程式碼範圍中的資料。 在 C++ 中, 變數 通常用來參考純量資料類型的實例,而其他類型的實例通常稱為 物件 。
物件 :為了簡單和一致性,本文會使用 字詞 物件 來參考類別或結構的任何實例。 一般意義上使用時,它包含所有類型,甚至是純量變數。
POD 類型 (一般舊資料):C++ 中這種非正式的資料類型類別是指純量的類型(請參閱基本類型一節)或 POD 類別 。 POD 類別沒有非 POD 的靜態資料成員,也沒有使用者定義的建構函式、使用者定義的解構函式或使用者定義指派運算子。 此外,POD 類別沒有虛擬函式、沒有基底類別,也沒有私用或受保護的非靜態資料成員。 POD 類型通常用於外部資料交換,例如以 C 語言撰寫的模組 (其中只有 POD 類型)。
指定變數和函式類型
C++ 既是 強型 別語言,也是 靜態型 別語言;每個物件都有一個類型,而且該類型永遠不會變更。 當您在程式碼中宣告變數時,您必須明確指定其類型,或使用 auto
關鍵字指示編譯器從初始化運算式推斷類型。 當您在程式碼中宣告函式時,您必須指定其傳回值的類型和每個引數的類型。 如果函式未傳回任何值,請使用傳回數值型別 void
。 例外狀況是當您使用函式範本時,允許任意類型的引數。
第一次宣告變數之後,您無法在稍後變更其類型。 不過,您可以將變數的值或函式的傳回值複製到另一個不同類型的變數。 這類作業稱為 類型轉換 ,有時是必要的,但也可能是資料遺失或不正確的潛在來源。
當您宣告 POD 類型的變數時,強烈建議您將 它初始化 ,這表示給予它初始值。 在您初始化變數以前,變數都會包含由先前遺留在該記憶體位置之任何位元所構成的「垃圾」值。 請務必記住 C++ 的一個重要層面,特別是如果您來自另一種處理初始化的語言。 當您宣告非 POD 類別類型的變數時,建構函式會處理初始化。
下列範例示範一些簡單的變數宣告,這些宣告各有一些描述。 這個範例也會示範編譯器如何使用類型資訊允許或不允許對變數進行某些後續作業。
int result = 0; // Declare and initialize an integer.
double coefficient = 10.8; // Declare and initialize a floating
// point value.
auto name = "Lady G."; // Declare a variable and let compiler
// deduce the type.
auto address; // error. Compiler cannot deduce a type
// without an intializing value.
age = 12; // error. Variable declaration must
// specify a type or use auto!
result = "Kenny G."; // error. Can't assign text to an int.
string result = "zero"; // error. Can't redefine a variable with
// new type.
int maxValue; // Not recommended! maxValue contains
// garbage bits until it is initialized.
基本 (內建) 類型
有別於某些程式語言,C++ 並沒有可衍生出所有其他類型的通用基底類型。 語言包含許多 基本類型 ,也稱為 內建類型 。 這些類型包括 、 int
double
、 long
、 bool
等數數值型別,以及 char
ASCII 和 UNICODE 字元的 和 wchar_t
類型。 大多數整數基本類型(除了 bool
、 double
wchar_t
和 相關類型以外)都有 unsigned
版本,可修改變數可儲存的值範圍。 例如, int
儲存 32 位帶正負號整數的 ,可以表示從 -2,147,483,648 到 2,147,483,647 的值。 unsigned int
也儲存為 32 位的 ,可以儲存從 0 到 4,294,967,295 的值。 每個案例中的可能值總數都相同;只有範圍不同。
編譯器會辨識這些內建類型,而且其內建規則可控管您可以對其執行的作業,以及如何轉換成其他基本類型。 如需內建類型及其大小和數值限制的完整清單,請參閱 內建類型 。
下圖顯示 Microsoft C++ 實作中內建類型的相對大小:
下表列出最常使用的基本類型,以及其在 Microsoft C++ 實作中的大小:
類型 | 大小 | 註解 |
---|---|---|
int |
4 個位元組 | 整數值的預設選項。 |
double |
8 個位元組 | 浮點值的預設選項。 |
bool |
1 個位元組 | 表示可以是 true 或 false 的值。 |
char |
1 個位元組 | 使用較舊 C-Style 字串或 std::string 物件中永遠不需要轉換成 UNICODE 之的 ASCII 字元。 |
wchar_t |
2 個位元組 | 表示可能以 UNICODE 格式 (在 Windows 上為 UTF-16,而其他作業系統可能不同) 編碼的「寬」字元值。 wchar_t 是 類型字串中使用的字元類型 std::wstring 。 |
unsigned char |
1 個位元組 | C++ 沒有內建位元組類型。 使用 unsigned char 來表示位元組值。 |
unsigned int |
4 個位元組 | 位元旗標的預設選項。 |
long long |
8 個位元組 | 表示更大的整數值範圍。 |
其他 C++ 實作可能會針對特定數數值型別使用不同的大小。 如需 C++ 標準需要的大小和大小關聯性的詳細資訊,請參閱 內建類型 。
型別 void
此 void
類型是特殊類型;您無法宣告 類型的 void
變數,但您可以宣告類型 void *
為 的變數(指標為 void
),有時在配置原始 (不具類型的) 記憶體時是必要的。 不過,的指標 void
不是型別安全,因此不建議在新式 C++ 中使用。 在函式宣告中,傳 void
回值表示函式不會傳回值;當做傳回型別使用是常見且可接受的用法 void
。 例如, fn(void)
雖然 C 語言所需的函式在參數清單中宣告零個參數 void
,但在新式 C++ 中不建議這種做法;應該宣告 fn()
無參數函式。 如需詳細資訊,請參閱 類型轉換和型別安全性 。
const
類型限定詞
任何內建或使用者定義型別都可以由 const
關鍵字限定。 此外,成員函式可以是 const
限定的,甚至是 const
多載。 在初始化類型之後,無法修改類型的值 const
。
const double PI = 3.1415;
PI = .75; //Error. Cannot modify const variable.
const
限定詞在函式和變數宣告中廣泛使用,而 「const correctness」 是 C++ 中的重要概念;基本上,它表示在編譯時期用來 const
保證不小心修改值。 如需詳細資訊,請參閱const
。
類型 const
與其非 const
版本不同,例如, const int
是與 int
不同的類型。 當您必須從變數中移除 const-ness 時,可以在這些罕見情況下使用 C++ const_cast
運算子。 如需詳細資訊,請參閱 類型轉換和型別安全性 。
字串類型
嚴格來說,C++ 語言沒有內建字串類型; char
並 wchar_t
儲存單一字元 - 您必須宣告這些類型的陣列,以近似字串,將終止的 Null 值(例如 ASCII '\0'
)新增至最後一個有效字元之後的陣列元素(也稱為 C 樣式字串 )。 C-Style 字串需要撰寫更多程式碼或使用外部字串公用程式庫函式。 但在新式 C++ 中,我們有標準程式庫類型 std::string
(適用于 8 位 char
類型字元字串)或 std::wstring
(針對 16 位 wchar_t
類型字元字串)。 這些 C++ 標準程式庫容器可以視為原生字串類型,因為它們是包含在任何符合規範 C++ 建置環境中之標準程式庫的一部分。 #include <string>
使用 指示詞,在您的程式中提供這些類型。 (如果您使用 MFC 或 ATL,類別 CString
也可供使用,但不是 C++ 標準的一部分。新式 C++ 不建議使用以 Null 結尾的字元陣列(先前提及的 C 樣式字串)。
使用者定義型別
當您定義 class
、 struct
、 union
或 enum
時,該建構會在程式碼的其餘部分使用,就好像它是基本類型一樣。 它在記憶體中的大小已知,而且套用了有關其在編譯時間檢查、執行階段和程式存留期之使用方式的特定規則。 基本內建類型和使用者定義類型之間的主要差異如下:
編譯器沒有使用者定義類型的內建知識。 它會在編譯器期間第一次遇到定義時,瞭解類型。
您會藉由定義 (透過多載) 適當的運算子做為類別成員或非成員函式,指定對類型執行哪些作業,以及如何轉換為其他類型。 如需詳細資訊,請參閱 函式多載
指標型別
如同最早的 C 語言版本,C++ 會繼續使用特殊宣告子 *
(星號)來宣告指標類型的變數。 指標類型會將在儲存實際資料值之位置的位址儲存在記憶體中。 在新式 C++ 中,這些指標類型稱為 原始指標 ,而且這些指標類型會透過特殊運算子在您的程式碼中存取: *
(星號)或 ->
(具有大於,通常稱為 箭 號的虛線)。 此記憶體存取作業稱為 取值 。 您使用的運算子取決於您要取值純量指標,還是物件中成員的指標。
處理指標類型一直以來都是 C 及 C++ 程式開發最具挑戰性和令人困惑的一面。 本節概述一些事實和做法,以協助您視需要使用原始指標。 不過,在新式 C++ 中,由於智慧型指標的 演進(本節結尾已討論更多內容),因此不再需要使用原始指標 進行物件擁有權。 使用原始指標觀察物件仍然很有用且安全。 不過,如果您必須將它們用於物件擁有權,您應該小心謹慎,並仔細考慮建立和終結它們所擁有的物件。
您應該知道的第一件事是,原始指標變數宣告只會配置足夠的記憶體來儲存位址:指標在取值時所參考的記憶體位置。 指標宣告不會配置儲存資料值所需的記憶體。 (該記憶體也稱為 備份存放區 。換句話說,藉由宣告原始指標變數,您會建立記憶體位址變數,而不是實際的資料變數。 如果您在確定它包含支援存放區的有效位址之前,先取值指標變數,就會在您的程式中造成未定義的行為(通常是嚴重錯誤)。 下列範例示範這種錯誤:
int* pNumber; // Declare a pointer-to-int variable.
*pNumber = 10; // error. Although this may compile, it is
// a serious error. We are dereferencing an
// uninitialized pointer variable with no
// allocated memory to point to.
這個範例會對指標類型取值,但不配置任何記憶體的來儲存指派給它的實際整數資料或有效記憶體位址。 下列程式碼示範這些錯誤:
int number = 10; // Declare and initialize a local integer
// variable for data backing store.
int* pNumber = &number; // Declare and initialize a local integer
// pointer variable to a valid memory
// address to that backing store.
...
*pNumber = 41; // Dereference and store a new value in
// the memory pointed to by
// pNumber, the integer variable called
// "number". Note "number" was changed, not
// "pNumber".
更正的程式碼範例會使用本機堆疊記憶體,建立 pNumber
所指向的備份存放區。 我們為了簡單起見使用基本類型。 實際上,指標的備份存放區通常是使用者定義型別,這些類型是使用 new
關鍵字運算式(在 C 樣式程式設計中,使用舊 malloc()
版 C 執行時間程式庫函式)動態配置於稱為堆積 的記憶體 區域(或 可用存放區 )。 配置之後,這些變數通常稱為 物件 ,特別是當這些變數是以類別定義為基礎時。 所配置的 new
記憶體必須由對應的 delete
語句刪除(或者,如果您使用 函 malloc()
式來配置它,C 執行時間函式 free()
)。
不過,很容易忘記刪除動態配置的物件,特別是在複雜的程式碼中,這會導致資源錯誤稱為 記憶體流失 。 基於這個理由,在新式 C++ 中不建議使用原始指標。 在智慧型指標中 包裝原始指標 幾乎總是更好,它會在叫用解構函式時自動釋放記憶體。 (也就是說,當程式碼超出智慧指標的範圍時。藉由使用智慧型指標,您幾乎可以消除 C++ 程式中的整個 Bug 類別。 下列範例中,假設 MyClass
是具有公用方法 DoSomeWork();
的使用者定義類型
void someFunction() {
unique_ptr<MyClass> pMc(new MyClass);
pMc->DoSomeWork();
}
// No memory leak. Out-of-scope automatically calls the destructor
// for the unique_ptr, freeing the resource.
如需智慧型指標的詳細資訊,請參閱 智慧型 指標。
如需指標轉換的詳細資訊,請參閱 類型轉換和型別安全性 。
如需一般指標的詳細資訊,請參閱 指標 。
Windows 資料類型
在 C 和 C++ 的傳統 Win32 程式設計中,大部分的函式會使用 Windows 特定的 typedefs 和 #define
宏(定義在 windef.h
中)來指定參數的類型和傳回值。 這些 Windows 資料類型大多是提供給 C/C++ 內建類型的特殊名稱(別名)。 如需這些 typedefs 和預處理器定義的完整清單,請參閱 Windows 資料類型 。 這些 typedefs 的其中一些,例如 HRESULT
和 LCID
,很實用且描述性。 其他,例如 INT
,沒有特殊意義,只是基本 C++ 類型的別名。 其他 Windows 資料類型有從 C 程式設計和 16 位元處理器時代保留下來的名稱,在現代硬體或作業系統上並無用處或意義。 也有與Windows 執行階段程式庫相關聯的特殊資料類型,列為 Windows 執行階段基底資料類型 。 在新式 C++ 中,一般指導方針是偏好 C++ 基本類型,除非 Windows 類型傳達有關如何解譯值的一些額外意義。
其他相關資訊
如需 C++ 類型系統的詳細資訊,請參閱下列文章。
值類型
描述 實值型別 及其使用相關問題。
類型轉換和型別安全性
描述一般類型轉換問題並顯示如何避免這些問題。
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應