1、首先是平凡的(trival)定義,通常一個平凡的類或者結構體需要滿足以下定義
(1).擁有平凡的默認構造函數和析構函數。默認的意思就是由編譯器為我們自動生成的,不許是我們自己定義的,而一旦定義了構造函數,即使函數體里沒有任何代碼,那么該構造函數也不在是平凡的,但是由於c++11提供了default,也可以是自己定義的加=default。
(2).擁有平凡的拷貝構造函數和移動構造函數。默認的意思同上,也可以使用=default。
(3).擁有平凡的拷貝賦值運算符和移動賦值運算符。
(4).不能包含虛函數和虛基類。
C++11中,我們使用模版類std::is_trivial<T>::value來判斷數據類型是否為平凡類型。
#include "stdafx.h" #include <iostream> using namespace std; class A { A(){} }; class B { B(B&){} }; class C { C(C&&){} }; class D { D operator=(D&){} }; class E { E operator=(E&&){} }; class F { ~F(){} }; class G { virtual void foo() = 0; }; class H : G {}; class I {}; int _tmain(int argc, _TCHAR* argv[]) { std::cout << std::is_trivial<A>::value << std::endl; //有不平凡的構造函數 std::cout << std::is_trivial<B>::value << std::endl; //有不平凡的拷貝構造函數 std::cout << std::is_trivial<C>::value << std::endl; //有不平凡的拷貝賦值運算符 std::cout << std::is_trivial<D>::value << std::endl; //有不平凡的拷貝賦值運算符 std::cout << std::is_trivial<E>::value << std::endl; //有不平凡的移動賦值運算符 std::cout << std::is_trivial<F>::value << std::endl; //有不平凡的析構函數 std::cout << std::is_trivial<G>::value << std::endl; //有虛函數 std::cout << std::is_trivial<H>::value << std::endl; //有虛基類 std::cout << std::is_trivial<I>::value << std::endl; //平凡的類return 0; }
2.接下來是標准布局的定義
(1).所有非靜態成員擁有相同的訪問級別,(訪問級別就是public,private,protected)
struct t1{ private : int a; public: int b; }; //不滿足標准布局,因為a,b訪問級別不同。
(2).在類和結構體繼承時需要滿足以下兩個情況之一:
派生類中有非靜態類,那么這個派生類只能有且只有一個僅包含了靜態成員的基類。
基類有非靜態成員,那么派生類中不允許有非靜態成員。
(這兩句話看着挺繞口,其實就是在說明一個事實,關於非靜態數據的事實,派生類中有非靜態的數據那么它的基類只能是只有靜態的,而且基類只能有一個。如果基類有非靜態的, 那么派生類就不能有非靜態的。有種蹺蹺板的感覺,非靜態的對面坐着的是靜態,父子類就 是坐在蹺蹺板的兩端這種對應關系。)(3).類中第一個非靜態類型與基類不是同一個類型。比如
struct A:B{ B b; int c; } //不符合這個條件。因為A中第一個成員是基類B類型的
(4).沒有虛類和虛基類(與trival中重復)
(5).所有非靜態數據成員都符合標准布局的要求,這其實就是一個遞歸的定義。
C++11中,我們使用模版類std::is_standard_layout<A>::value來判斷類型是否是一個標准布局類型。
所以在C++11中,POD就是滿足平凡的(trival)和標准布局(standard layout)這兩個方面。

#include <iostream> using namespace std; class A { private: int a; public: int b; }; class B1 { static int x1; }; class B2 { int x2; }; class B : B1, B2 { int x; }; class C1 {}; class C : C1 { C1 c; }; class D { virtual void foo() = 0; }; class E : D {}; class F { A x; }; int main() { std::cout << std::is_standard_layout<A>::value << std::endl; //違反定義1,成員a和b具有不同的訪問權限 std::cout << std::is_standard_layout<B>::value << std::endl; //違反定義2,繼承樹有兩個(含)以上的類有非靜態成員 std::cout << std::is_standard_layout<C>::value << std::endl; //違反定義3,第一個非靜態成員是基類類型 std::cout << std::is_standard_layout<D>::value << std::endl; //違反定義4,有虛函數 std::cout << std::is_standard_layout<E>::value << std::endl; //違反定義5,有虛基類 std::cout << std::is_standard_layout<F>::value << std::endl; //違反定義6,非靜態成員x不符合標准布局類型 return 0; }

#include <iostream> #include <cstring> using namespace std; class AA { public: int x; double y; }; int main() { if (std::is_pod<AA>::value) { cout << "before" << endl; AA aa; aa.x = 10; aa.y = 20.0f; cout << aa.x << " " << aa.y << endl; size_t size = sizeof(aa); char *p = new char[size]; memcpy(p, &aa, size); AA *pA = (AA*)p; cout << "after" << endl; cout << pA->x << " " << pA->y << endl; delete p; } return 0; }
說了這么多,那么為什么我們需要POD這種條件滿足的數據呢?
(1).可以使用字節賦值,比如memset,memcpy操作
(2).對C內存布局兼容。
(3).保證了靜態初始化的安全有效。