平凡类型

**Plain old data structure**, 縮寫為 **POD**, 是 C++ 語言的標準中定義的一類資料結構，POD 適用於需要明確的資料底層操作的系統中。POD 通常被用在系統的邊界處，即指不同系統之間只能以底層資料的形式進行互動，系統的高層邏輯不能互相容。比如當物件的欄位值是從外部資料中構建時，系統還沒有辦法對物件進行語意檢查和解釋，這時就適用 POD 來儲存資料。

## 定義

POD 類型包括下述 C++ 類型，以及其 cv-qualified 的類型，還有以其為基本類型的陣列類型:

* 純量類型 (scalar type)
* POD 類類型 (POD class type)

### 標量類型

術語標量類型包括下述 C++ 類型範疇，以及其 cv-qualified 類型:

* 算術類型 (arithmetic type)
* 列舉類型 (enumeration type)
* 指標類型 (pointer type)
* 指標到成員類型 (pointer-to-member type)

術語算術類型包括下述 C++ 類型範疇:

* 整數類型 (integral type)
* 浮點類型 (floating type)

術語整數類型包括下述 C++ 類型範疇:

* 有符號整數類型 (signed char, short, int, long),
* 無符號整數類型 (unsigned char, unsigned short, unsigned int, unsigned long),
* 字元類型 char 與寬字元類型 wchar\_t
* 布林類型 bool。

術語浮點類型包括 C++ 的 float, double, and long double 類型.

術語列舉類型包括各種列舉類型，即命名的常數值 (named constant values) 的集合.

術語指標類型包括下述 C++ 類型範疇:

* 空指標 pointer-to-void (void \*),
* 物件指標 pointer-to-object 與指向靜態數據成員的指標 pointer-to-static-member-data (都是形如為 T\*，其中 T 是物件類型),
* 函式指標 pointer-to-function 與指向靜態成員函式的指標 pointer-to-static-member-function (都是形如 T (\*)(...)，T 是函式的返回值的類型).

術語指標到成員類型包括下述 C++ 類型範疇:

* 指標到非靜態數據成員 (pointer-to-nonstatic-member-data), 形如 T C::\* 表示指向類 C 的類型為 T 的數據成員的指標；
* 指標到非靜態成員函式 (pointer-to-nonstatic-member-functions), 形如 T (C::\*)(...) 表示指向類 C 的返回值類型為 T 的成員函式的指標.

### POD 類類型

POD 類類型是指聚合類 (aggregate classes, 即 POD-struct types) 與聚合 union (POD-union types)，且不具有下述成員:

* 指標到成員類型的非靜態資料成員 (包括陣列)。
* 非 POD 類類型的非靜態資料成員 (包括陣列)。
* 參照類型的 (reference type) 非靜態資料成員。
* 使用者定義的拷貝與賦值算子。
* 使用者定義的解構函式。

術語聚合是指任何的陣列或者類，且不具有下述特徵:

* 使用者定義的建構函式。
* 私有或保護的非靜態資料成員。
* 基礎類別。
* 虛擬函式。

可見，POD 類類型就是指 class、struct、union，且不具有使用者定義的建構函式、解構函式、拷貝算子、賦值算子；不具有繼承關係，因此沒有基礎類別；不具有虛擬函式，所以就沒有虛表；非靜態資料成員沒有私有或保護屬性的、沒有參照類型的、沒有非 POD 類類型的（即巢狀類都必須是 POD）、沒有指標到成員類型的（因為這個類型內含了 this 指標）。

### C++11

C++11 把情況推廣為兩種：

類型是平凡的（trivial），則可以靜態初始化、可以用 memcpy 直接複製資料而不是必須用 copy 建構函式。其生存期始於它的物件的儲存被定義，無須等到建構函式完成。平凡 class 或結構必須滿足：

* 有平凡的預設建構函式，可用這樣的預設語法：(SomeConstructor () = default;)
* 有平凡的 copy 與 move 建構函式，可用預設語法.
* 有平凡的 copy 與 move 運算子，可用預設語法.
* 有平凡的 destructor，不能是虛擬函式.

建構函式是平凡的，僅當類沒有虛成員函式也沒有虛基礎類別。Copy/move 運算子是平凡的，僅當沒有靜態資料成員。

類型是標準布局的（standard-layout）意味著它是有序的並且安排其成員相容於 C 語言。這要求滿足：

* 沒有虛擬函式
* 沒有虛基礎類別
* 所有非靜態資料成員有相同的存取控制 (public, private, protected)
* 所有非靜態資料成員，包括在任何基礎類別中的，存在於類繼承體系中的一個類中
* 上述規則適用於所有基礎類別與類繼承體系中的所有非靜態資料成員
* 沒有同一類型的基本類型被定義為第一個非靜態資料成員

一個 class/struct/union 是 POD，當它是平凡的、標準布局的，所有資料成員是 POD.

分為兩個概念，物件可以不滿足其中一個但是滿足另外一個。例如，類有複雜的 move 與 copy 建構函式，因此不是平凡的，但可能是標準布局因此能與 C 程式互操作。類似地，一個類的有 public 與 private 的非靜態資料成員，因此不是標準布局，但可以是平凡的因此可以 memcpy 操作。

## 用途

POD 類型在原始碼相容於 ANSI C 時非常重要。POD 物件與 C 語言的對應物件具有共同的一些特性，包括初始化、複製、記憶體布局、定址。

一個例子是下述 C++ 的 new 表達式中的物件初始化，POD 與 non-POD 的區別:

因此，non-POD 類型的物件或陣列總是被初始化；而 POD 類型的物件或陣列可能未被初始化.

其它與 POD 相關的 C++ 特性:

* 記憶體布局 ——POD 物件的組成位元組是連續的.

> "POD-struct ... types are layout-compatible if they have the same number of members, and corresponding members (in order) have layout-compatible types".
> 
> 

> POD-union ... types are layout-compatible if they have the same number of members, and corresponding members (in any order) have layout-compatible types".  
> 
> 

* 初始化 —— 對於 non-const POD 物件，如果沒有初始化聲明時，具有不確定的初值 (indeterminate initial value) . POD 物件的預設初始化為 0 值。靜態 POD 物件初始化為給定的初值，如果是局部靜態 POD 物件，在進入所在作用域之前初始化 [§6.7, ¶4]; 對於非局部靜態 POD 物件，在任何動態初始化之前賦予初值.
* 拷貝 ——POD 物件可直接拷貝 (例如用 memcpy ()) 到其它字元陣列或相同 POD 類型的物件，保持其值不變。POD 類型可以用作標準模板字串類的字元。由於這個原因，函式的返回值如果是 non-POD 類型，則不能通過暫存器傳遞函式的返回值。
* 定址 —— 一個 POD 物件的位址可以是一個位址常數表達式；一個對 POD 成員的參照可以是一個參照常數表達式。一個 POD-struct 物件的指標，適合用 reinterpret\_cast 轉換到它的初始值.

## POD JAVA

JAVA 中，一些開發者認為 POD 類型是符合沒有 public 成員且沒有方法的類，比如 data transfer object。其實不使用事件控制代碼並且不實現除 getter 和 setter 之外的附加方法的 POJO（只含有 getter 和 setter 的類）和 JAVA Bean 也屬於 POD。但不管怎麼樣，POJO 和 JAVA Bean 已經有了封裝，已經違反了 POD 的定義了。

## 參見

Visual C++ 名字修飾

## 參考文獻