類型的概念在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
。 例如,雖然 C 語言需要參數在參數清單中宣告 void
零個參數的函式, fn(void)
但在新式C++中不建議這種做法;應該宣告 fn()
無參數函式。 如需詳細資訊,請參閱 類型轉換和類型安全性。
const
類型限定元
任何內建或使用者定義型別都可以由 const
關鍵詞限定。 此外,成員函式可以是 const
限定的,甚至是 const
多載。 在初始化類型之後,無法修改類型的值 const
。
const double PI = 3.1415;
PI = .75; //Error. Cannot modify const variable.
const
限定符在函式和變數宣告中廣泛使用,而“const 正確性”是C++的重要概念;基本上,它表示在編譯時期用來const
保證不小心修改值。 如需詳細資訊,請參閱const
。
類型 const
與其非const
版本不同,例如, const int
是與 int
不同的類型。 當您必須從變數中移除 const_cast
時,可以在這些罕見情況下使用 C++ 運算符。 如需詳細資訊,請參閱 類型轉換和類型安全性。
字串類型
嚴格來說,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
所指向的備份存放區。 我們為了簡單起見使用基本類型。 實際上,指標的備份存放區通常是使用者定義型別,這些類型是使用關鍵詞表達式(在 C 樣式程式設計中,使用舊版 C 運行時間連結庫函式)動態配置於稱為堆積new
區域(或malloc()
)。 配置之後,這些變數通常稱為 物件,特別是當這些變數是以類別定義為基礎時。 所配置的 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 執行階段 Library 相關聯的特殊數據類型,列為 Windows 執行階段 基底數據類型。 在現代C++中,一般指導方針是偏好C++基本類型,除非 Windows 類型傳達一些關於如何解譯值的額外意義。
其他相關資訊
如需C++類型系統的詳細資訊,請參閱下列文章。
值類型
描述 實值型別 及其使用相關問題。
類型轉換和類型安全性
描述一般類型轉換問題並顯示如何避免這些問題。