1. POD 类型介绍

        在 C++ 里,POD 是 “Plain Old Data” 的缩写,意思是普通旧数据。在 C++11 之前,POD 类型就已存在,不过 C++11 对其定义和规则进行了更为细致的划分和明确。POD 类型旨在和 C 语言中的数据类型保持兼容,保证数据在内存中的布局和 C 语言里的一致,这有利于在 C++ 和 C 代码之间进行数据交互,同时也能确保对象在内存中的存储形式简单且可预测。

2. C++11 中 POD 类型的分类

        C++11 把 POD 类型细分为平凡类型(Trivial Type)标准布局类型(Standard Layout Type)一个类型要是同时属于平凡类型和标准布局类型,那它就是 POD 类型

2.1 平凡类型(Trivial Type)

平凡类型需满足以下条件:
        平凡默认构造函数:类或者结构体必须有平凡默认构造函数。平凡默认构造函数指的是编译器自动生成的默认构造函数,也就是没有用户自定义的默认构造函数。

// 有平凡默认构造函数
struct TrivialStruct1 {
    int x;
    double y;
}; 

// 没有平凡默认构造函数
struct NonTrivialStruct1 {
    NonTrivialStruct1() : x(0) {}  // 用户自定义默认构造函数
    int x;
};

        平凡拷贝构造函数:必须有平凡拷贝构造函数,即编译器自动生成的拷贝构造函数,没有用户自定义的拷贝构造函数。

// 有平凡拷贝构造函数
struct TrivialStruct2 {
    int a;
};

// 没有平凡拷贝构造函数
struct NonTrivialStruct2 {
    NonTrivialStruct2(const NonTrivialStruct2& other) : a(other.a) {}  // 用户自定义拷贝构造函数
    int a;
};

        平凡移动构造函数:需要有平凡移动构造函数,也就是编译器自动生成的移动构造函数,没有用户自定义的移动构造函数。

// 有平凡移动构造函数
struct TrivialStruct3 {
    int b;
};

// 没有平凡移动构造函数
struct NonTrivialStruct3 {
    NonTrivialStruct3(NonTrivialStruct3&& other) : b(other.b) {}  // 用户自定义移动构造函数
    int b;
};

        平凡拷贝赋值运算符:必须有平凡拷贝赋值运算符,即编译器自动生成的拷贝赋值运算符,没有用户自定义的拷贝赋值运算符。

// 有平凡拷贝赋值运算符
struct TrivialStruct4 {
    int c;
};

// 没有平凡拷贝赋值运算符
struct NonTrivialStruct4 {
    NonTrivialStruct4& operator=(const NonTrivialStruct4& other) {
        c = other.c;
        return *this;
    }
    int c;
};

        平凡移动赋值运算符:要有平凡移动赋值运算符,即编译器自动生成的移动赋值运算符,没有用户自定义的移动赋值运算符。

// 有平凡移动赋值运算符
struct TrivialStruct5 {
    int d;
};

// 没有平凡移动赋值运算符
struct NonTrivialStruct5 {
    NonTrivialStruct5& operator=(NonTrivialStruct5&& other) {
        d = other.d;
        return *this;
    }
    int d;
};

        平凡析构函数:必须有平凡析构函数,即编译器自动生成的析构函数,没有用户自定义的析构函数。

// 有平凡析构函数
struct TrivialStruct6 {
    int e;
};

// 没有平凡析构函数
struct NonTrivialStruct6 {
    ~NonTrivialStruct6() {
        // 用户自定义析构函数代码
    }
    int e;
};
2.1 标准布局类型(Standard Layout Type)

标准布局类型需满足以下条件:

        没有虚函数和虚基类:类或者结构体不能包含虚函数和虚基类,因为虚函数和虚基类会让对象的内存布局变得复杂,破坏和 C 语言数据布局的兼容性。

// 标准布局类型
struct StandardLayoutStruct1 {
    int x;
    double y;
};

// 非标准布局类型
struct NonStandardLayoutStruct1 {
    virtual void func() {}  // 包含虚函数
    int z;
};

        所有非静态数据成员都有相同的访问控制:所有非静态数据成员要么都是 public,要么都是 protected,要么都是 private。

// 标准布局类型
struct StandardLayoutStruct2 {
    int a;
    double b;
};

// 非标准布局类型
struct NonStandardLayoutStruct2 {
private:
    int c;
public:
    double d;
};

        基类和非静态数据成员的排列规则:第一、当一个类从没有非静态数据成员的基类继承时,它可以拥有自己的非静态数据成员。第二、一个类不能同时从多个拥有非静态数据成员的基类继承。

// 第一
// 标准布局类型
struct Base1 {}; // Base1 没有非静态数据成员
struct StandardLayoutStruct3 : Base1 {
    int e; // StandardLayoutStruct3 可以有自己的非静态数据成员
};

// 第二
// 非标准布局类型
struct Base2 { int f; }; // Base2 有非静态数据成员
struct Base3 { int g; }; // Base3 有非静态数据成员
struct NonStandardLayoutStruct3 : Base2, Base3 {
    int h;
};

3. POD 类型的优势

        内存布局可预测:POD 类型的对象在内存中的布局和 C 语言里的一致,这使得在进行内存操作(像 memcpy、memset 等)时更为安全和高效。

#include <iostream>
#include <cstring>

struct PODStruct {
    int x;
    double y;
};

int main() {
    PODStruct obj1 = {10, 3.14};
    PODStruct obj2;
    std::memcpy(&obj2, &obj1, sizeof(PODStruct));
    std::cout << obj2.x << " " << obj2.y << std::endl;
    return 0;
}

        和 C 语言的兼容性:由于 POD 类型和 C 语言的数据类型兼容,所以在 C++ 和 C 代码之间传递数据变得更加容易。
        序列化和反序列化:POD 类型对象的简单内存布局让序列化和反序列化操作变得更简单,可直接将对象的内存数据写入文件或者网络,反之亦然。

4. 判断类型是否为 POD 类型

        在 C++ 中,可以使用 std::is_pod 这个类型特征来判断一个类型是否为 POD 类型。

#include <iostream>
#include <type_traits>

struct PODType {
    int a;
    double b;
};

struct NonPODType {
    virtual void func() {}
};

int main() {
    std::cout << std::boolalpha;
    std::cout << "Is PODType a POD type? " << std::is_pod<PODType>::value << std::endl;
    std::cout << "Is NonPODType a POD type? " << std::is_pod<NonPODType>::value << std::endl;
    return 0;
}

        在上述代码里,std::is_pod<PODType>::value 会返回 true,表明 PODType 是 POD 类型;而 std::is_pod<NonPODType>::value 会返回 false,表明 NonPODType 不是 POD 类型。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐