C++11 POD類型


POD,全稱plain old data,plain代表它是一個普通類型,old代表它可以與c兼容,可以使用比如memcpy()這類c中最原始函數進行操作。C++11中把POD分為了兩個基本概念的集合,即:平凡的(trival)和標准布局的(standard layout)。

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)這兩個方面。
可以使用<type_traits>中的is_pod<T>::value判斷T是不是POD類型的。
#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;
}
View Code
#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;
}
View Code

 

說了這么多,那么為什么我們需要POD這種條件滿足的數據呢?

  (1).可以使用字節賦值,比如memset,memcpy操作

  (2).對C內存布局兼容。

  (3).保證了靜態初始化的安全有效。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM