【1】什么是POD類型?
Plain old data structure,縮寫為POD,Plain代表是一種普通類型,Old體現該類型的對象可以與C兼容。
POD類型是C++語言標准中定義的一類數據結構,適用於需要明確的數據底層操作的系統中。
POD通常被用在系統的邊界處,即指不同系統之間只能以底層數據的形式進行交互,系統的高層邏輯不能互相兼容。
比如,當對象的字段值是從外部數據中構建時,系統還沒有辦法對對象進行語義檢查和解釋,這時就適用POD來存儲數據。
嚴格來講,一種既是普通類型(Trivial Type)又是標准布局類型(Standard-layout Type)的類型,即是POD類型。
通俗地講,一種類型的對象通過二進制拷貝(如memcpy())后還能保持數據不變可正常使用的類型,即是POD類型。
【2】為什么引入POD類型?
不同類型的對象,本質區別在於對象的成員在內存中的布局是不同的。
在某些情況下,布局是有明確規范的定義,但如果類或結構包含某些C++語言功能,如虛基類、虛函數、具有不同的訪問控制的成員等,
則不同編譯器會有不同的布局實現,具體取決於編譯器對代碼的優化方式,比如實現內存對齊,減少訪存指令周期等。
例如,如果類具有虛函數,該類的所有實例都會包含一個指向虛函數表的指針,那么這個對象就不能直接通過二進制拷貝的方式傳到其它語言編程的程序中使用。
C++給定對象的類型取決於其特定的內存布局方式,一個對象是普通、標准布局還是POD類型,可以根據標准庫函數模板來判斷:
1 #include <type_traits> 2 #include <iostream> 3 using namespace std; 4 5 int main() 6 { 7 cout << is_trivial<int>::value << endl; // 1 8 cout << is_standard_layout<int>::value << endl; // 1 9 cout << is_pod<int>::value << endl; // 1 10 11 system("pause"); 12 }
POD對象與C語言中的對象具有一些共同的特性,包括初始化、復制、內存布局與尋址:
(1)可以使用字節賦值,比如用memset、memcpy對POD類型進行賦值操作;
(2)對C內存布局兼容,POD類型的數據可以使用C函數進行操作且總是安全的;
(3)保證了靜態初始化的安全有效,靜態初始化可以提高性能,如將POD類型對象放入BSS段默認初始化為0。
【3】POD類型需要滿足什么條件?
POD類型既要滿足平凡類型的條件又要滿足標准布局類型的條件。
(1)平凡類型
[1] 有平凡的構造函數
[2] 有平凡的拷貝構造函數
[3] 有平凡的移動構造函數
[4] 有平凡的拷貝賦值運算符
[5] 有平凡的移動賦值運算符
[6] 有平凡的析構函數
[7] 不能包含虛函數
[8] 不能包含虛基類
如下代碼示例:
1 #include <type_traits> 2 #include <iostream> 3 using namespace std; 4 5 class A { A() {} }; 6 class B { B(B&) {} }; 7 class C { C(C&&) {} }; 8 class D { D operator=(D&) {} }; 9 class E { E operator=(E&&) {} }; 10 class F { ~F() {} }; 11 class G { virtual void foo() = 0; }; 12 class H : G {}; 13 class I {}; 14 15 int main() 16 { 17 std::cout << std::is_trivial<A>::value << std::endl; // 有不平凡的構造函數 18 std::cout << std::is_trivial<B>::value << std::endl; // 有不平凡的拷貝構造函數 19 std::cout << std::is_trivial<C>::value << std::endl; // 有不平凡的拷貝賦值運算符 20 std::cout << std::is_trivial<D>::value << std::endl; // 有不平凡的拷貝賦值運算符 21 std::cout << std::is_trivial<E>::value << std::endl; // 有不平凡的移動賦值運算符 22 std::cout << std::is_trivial<F>::value << std::endl; // 有不平凡的析構函數 23 std::cout << std::is_trivial<G>::value << std::endl; // 有虛函數 24 std::cout << std::is_trivial<H>::value << std::endl; // 有虛基類 25 26 std::cout << std::is_trivial<I>::value << std::endl; // 平凡的類 27 28 system("pause"); 29 return 0; 30 } 31 32 /*運行結果 33 0 34 0 35 0 36 0 37 0 38 0 39 0 40 0 41 1 42 */
(2)標准布局定義
[1] 所有非靜態成員有相同的訪問權限
[2] 繼承樹中最多只能有一個類有非靜態數據成員
[3] 子類的第一個非靜態成員不可以是基類類型
[4] 沒有虛函數
[5] 沒有虛基類
[6] 所有非靜態成員都符合標准布局類型
如下代碼示例:
1 #include <type_traits> 2 #include <iostream> 3 using namespace std; 4 5 class A 6 { 7 private: 8 int a; 9 public: 10 int b; 11 }; 12 13 class B1 14 { 15 static int x1; 16 }; 17 18 class B2 19 { 20 int x2; 21 }; 22 23 class B : B1, B2 24 { 25 int x; 26 }; 27 28 class C1 {}; 29 class C : C1 30 { 31 C1 c; 32 }; 33 34 class D { virtual void foo() = 0; }; 35 class E : D {}; 36 class F { A x; }; 37 38 int main() 39 { 40 std::cout << std::is_standard_layout<A>::value << std::endl; // 違反定義1:成員a和b具有不同的訪問權限 41 std::cout << std::is_standard_layout<B>::value << std::endl; // 違反定義2:繼承樹有兩個(含)以上的類有非靜態成員 42 std::cout << std::is_standard_layout<C>::value << std::endl; // 違反定義3:第一個非靜態成員是基類類型 43 std::cout << std::is_standard_layout<D>::value << std::endl; // 違反定義4:有虛函數 44 std::cout << std::is_standard_layout<E>::value << std::endl; // 違反定義5:有虛基類 45 std::cout << std::is_standard_layout<F>::value << std::endl; // 違反定義6:非靜態成員x不符合標准布局類型 46 47 system("pause"); 48 return 0; 49 } 50 51 /*運行結果 52 0 53 0 54 0 55 0 56 0 57 0 58 */
【4】POD類型的應用
當一個數據類型滿足了“平凡的定義”和“標准布局定義”,我們則認為它是一個POD數據。
一個POD類型是可以進行二進制拷貝的,如下代碼示例:
1 #include <type_traits> 2 #include <iostream> 3 using namespace std; 4 5 class A 6 { 7 public: 8 int x; 9 double y; 10 }; 11 12 int main() 13 { 14 if (std::is_pod<A>::value) 15 { 16 std::cout << "before" << std::endl; 17 A a; 18 a.x = 8; 19 a.y = 10.5; 20 std::cout << a.x << std::endl; 21 std::cout << a.y << std::endl; 22 23 size_t size = sizeof(a); 24 char* p = new char[size]; 25 memcpy(p, &a, size); 26 A* pA = (A*)p; 27 28 std::cout << "after" << std::endl; 29 std::cout << pA->x << std::endl; 30 std::cout << pA->y << std::endl; 31 32 delete p; 33 } 34 35 system("pause"); 36 return 0; 37 } 38 39 /*運行結果 40 before 41 8 42 10.5 43 after 44 8 45 10.5 46 */
很明顯,對一個POD類型的對象進行二進制拷貝后,數據可成功遷移。
good good study, day day up.
順序 選擇 循環 總結