POD (程式設計)
Plain old data structure, 縮寫為POD, 是C++語言的標準中定義的一類數據結構[1],POD適用於需要明確的數據底層操作的系統中。POD通常被用在系統的邊界處,即指不同系統之間只能以底層數據的形式進行互動,系統的高層邏輯不能互相容。比如當對象的欄位值是從外部數據中構建時,系統還沒有辦法對對象進行語意檢查和解釋,這時就適用POD來儲存數據。
定義
POD類型包括下述C++類型,以及其cv-qualified的類型,還有以其為基本類型的陣列類型[2]:
- 純量類型(scalar type)
- POD類類型(POD class type)
標量類型
術語標量類型包括下述C++類型範疇, 以及其cv-qualified類型[3]:
- 算術類型(arithmetic type)
- 列舉類型(enumeration type)
- 指標類型(pointer type)
- 指標到成員類型(pointer-to-member type)
術語算術類型包括下述C++類型範疇[4]:
- 整數類型(integral type)
- 浮點類型(floating type)
術語整數類型包括下述C++類型範疇[5]:
- 有符號整數類型 (signed char, short, int, long),
- 無符號整數類型(unsigned char, unsigned short, unsigned int, unsigned long),
- 字元類型char與寬字元類型wchar_t
- 布林類型bool。
術語浮點類型包括C++的float, double, and long double類型[4].
術語列舉類型包括各種列舉類型,即命名的常數值(named constant values)的集合[6].
術語指標類型包括下述C++類型範疇[7]:
- 空指標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++類型範疇[7]:
- 指標到非靜態數據成員(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),且不具有下述成員[8]:
- 指標到成員類型的非靜態數據成員(包括陣列)。
- 非POD類類型的非靜態數據成員(包括陣列)。
- 參照類型的(reference type)非靜態數據成員。
- 用戶定義的拷貝與賦值算子。
- 用戶定義的解構函式。
術語聚合是指任何的陣列或者類,且不具有下述特徵[9]:
- 用戶定義的建構函式。
- 私有或保護的非靜態數據成員。
- 基礎類別。
- 虛擬函式。
可見,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的區別[10]:
表達式 | POD類型T | non-POD類型T |
---|---|---|
new T | 不初始化 | 預設初始化 |
new T() | 總是預設初始化 | |
new T(x) | 總是呼叫建構函式初始化 |
因此,non-POD類型的對象或陣列總是被初始化;而POD類型的對象或陣列可能未被初始化.
其它與POD相關的C++特性:
- 主記憶體佈局——POD對象的組成位元組是連續的[11].
"POD-struct ... types are layout-compatible if they have the same number of members, and corresponding members (in order) have layout-compatible types"[12].
POD-union ... types are layout-compatible if they have the same number of members, and corresponding members (in any order) have layout-compatible types"[13].
- 初始化——對於non-const POD對象,如果沒有初始化聲明時,具有不確定的初值(indeterminate initial value) [14]. POD對象的預設初始化為0值[15]. 靜態POD對象初始化為給定的初值,如果是局部靜態POD對象,在進入所在作用域之前初始化[16][§6.7, ¶4]; 對於非局部靜態POD對象,在任何動態初始化之前賦予初值[17].
- 拷貝——POD對象可直接拷貝(例如用memcpy())到其它字元陣列或相同POD類型的對象,保持其值不變[18]。POD類型可以用作標準模板字串類的字元[19]. 由於這個原因,函數的返回值如果是non-POD類型,則不能通過暫存器傳遞函數的返回值。
- 定址——一個POD對象的地址可以是一個地址常數表達式[20];一個對POD成員的參照可以是一個參照常數表達式[21]. 一個POD-struct對象的指標,適合用reinterpret_cast轉換到它的初始值[22].
POD JAVA
JAVA中,一些開發者認為POD類型是符合沒有public成員且沒有方法的類,比如data transfer object。其實不使用事件控制代碼並且不實現除getter和setter之外的附加方法的POJO(只含有getter和setter的類)和JAVA Bean也屬於POD。但不管怎麼樣,POJO和JAVA Bean已經有了封裝,已經違反了POD的定義了。
參見
參考文獻
- ^ ISO/IEC 14882, first edition, 1998-09-01 p. 5, footnote 4]
- ^ 參見C++標準的§3.9, ¶10; §9, ¶4
- ^ 參見C++標準§3.9, ¶10
- ^ 4.0 4.1 參見C++標準§3.9.1, ¶8
- ^ 參見C++標準§3.9.1, ¶7
- ^ 參見C++標準§3.9.1, ¶1; §7.2, ¶1
- ^ 7.0 7.1 參見C++標準§3.9.2, ¶1
- ^ 參見C++標準§9, ¶4
- ^ 參見C++標準§8.5.1, ¶1
- ^ 參見C++標準§5.3.4, ¶15
- ^ 參見C++標準§1.8, ¶5
- ^ 參見C++標準§9.2, ¶14
- ^ 參見C++標準§9.2, ¶15
- ^ 參見C++標準§8.5, ¶9
- ^ 參見C++標準§8.5, ¶5
- ^ 參見C++標準§6.7, ¶4
- ^ 參見C++標準§3.6.2, ¶1
- ^ 參見C++標準§3.9, ¶2與3
- ^ 參見C++標準§21, ¶1
- ^ 參見C++標準§5.19, ¶4
- ^ 參見C++標準§5.19, ¶5
- ^ 參見C++標準§9.2, ¶17