說說C++中的POD

背景

POD的全稱是Plain Old Data,這個Old就是體現了C語言的兼容。POD數據類型就是兼容性很重要的體現,特別是對用戶自定義的類型(struct,class定義的類)。因爲標準要求POD類型的內存佈局是完全與C語言。

原理

C++是C的超集,加入了更多語法層面的新機制,編譯器充當了一個翻譯的角色


struct POD
{
    int v;
    int a;
};

struct NotPOD
{
    public:
        NotPOD(){}
    
        int v;
        int a;
};

上面的NotPOD結構體,表面上看只是比結構體POD多定義了一個構造函數。但當編譯器將其翻譯成彙編時,對NotPOD的構造函數,是產生了額外的代碼(<< 深入探索C++對象模型 >>中有深入講解),所以它的內存佈局肯定跟POD結構體就不一樣了。最典型的虛函數,是編譯器產生一個VTABLE去實現,所以有虛函數的結構體必然不會跟C語言的內存佈局一致。

所以在C++標準中定義的POD類型標準,其實就是編譯器不會生成額外代碼的準則。

POD類型

每個版本標準的POD類型都不一樣,這也是C++ 的繁瑣之處,作爲C語言的超集,揹負了兼容C語言的包袱,每次版本更新,這種兼容性語法必然會更新。其實作爲一個開發者,應該不需要,也不應該去關注這些細節。這也是C++被詬病的地方。

POD類型的語法實在讓人覺得繁瑣,枯燥。這裏介紹C++ 11中爲POD類型(C++ 11標準是C++ 走向現代語言的一個重要標識)提供幾個檢測的模板類,再掌握幾個簡單經驗,則足以寫出 POD類型。

類型判斷工具

  • template < typename T> struct std:: is_ pod;

這個工具直接判斷類型T是否是POD類型,最常用,最有用

示例代碼

#include <iostream>
struct D3
{
    D3()
    {
        a = 1;
        b = 1;
        c = 1;
        d = 1;
    }

    int a;
    int b;
    int c;
    int d;
};

struct D4
{
    int v;
    int a;
};


int main()
{

    std::cout << std::is_pod<D3>::value << std::endl;
    std::cout << std::is_pod<D4>::value << std::endl;
}
  • template < typename T> struct std:: is_ trivial;

判斷是否平凡類型,這是C++ 11中對POD類型引入的一個新的概念,滿足了trivial類型,纔是POD類型

#include <iostream>
struct D3
{
    D3()
    {
        a = 1;
        b = 1;
        c = 1;
        d = 1;
    }

    int a;
    int b;
    int c;
    int d;
};

struct D4
{
    int v;
    int a;
};

int main()
{

    std::cout << std::is_trivial<D3>::value << std::endl;
    std::cout << std::is_trivial<D4>::value << std::endl;
}

幾個經驗

  1. C++中所有的基礎類型(int,char,double,指針等類型)都是POD類型
  2. 基礎類型的數組都是POD類型
  3. STL中的array爲POD類型,其餘的STL容器都是非POD類型
  4. 以struct來定義自定義類型。
  5. 不要定義構造,析構函數,複製構造函數,賦值函數,移動構造函數
  6. 不要定義類非靜態方法
  7. 類的成員不要出現非POD類型
  8. 不要使用繼承(隨意在特定條件下,有繼承的類型可以POD類型,但是建議還是不要使用,很容易出錯)

綜上幾點,其實最簡單,兼容最強(不同的編譯器的內存佈局的處理方法可能會不一樣)的就是以C語言struct的方式使用struct(只有基本數據類型)。

一個比較典型的例子

#include <iostaream>
struct sTest
{
    std::string str;
    int v;
}

int main()
{
    sTest t;
    memset(&t,0,sizeof(sTest));
}

上面的代碼,sTest不是POD類型,因爲string不是POD類型。直接通過memset去初始化t對象,是會崩潰的。這是個很容易犯錯誤的地方,有些程序員,覺得STL中的string比C語言的char方便,所以在結構體使用string,然後還是以memset初始化。
在C++ 中,非POD類型是不能使用memset和memcpy函數的

POD類型的用處

  • 內存佈局跟C語言一樣,用於C 庫的接口。
  • C++ 開發的庫,對外提供的數據類型都應該是POD類型。尤其是用於C語言或.Net的C++庫。
  • 可以直接用memeset初始化,或用memcpy拷貝
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章