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


所有评论(0)