什么是POD类型
POD的全称叫做Plain Old Data,简单讲就是一个类或者一个结构体通过二进制拷贝之后还能保持其不变,那么这个类型就是POD类型。
什么类型属于POD类型
当一个类型具有平凡的定义和标准布局这两种情况时,才可以说这种类型是POD类型。
什么是平凡的定义的类型
当一个类具有以下的几个定义时,才可以说是平凡的类:
拥有平凡的默认构造函数和析构函数
这里的平凡的默认构造函数是指在不定义构造函数的情况下,编译器默认生成的构造函数。如果自己定义了构造函数,但是什么都没有做,仍然不是平凡的构造函数,可以使用=default来定义。拥有平凡的拷贝构造函数和移动构造函数
- 拥有平凡的拷贝赋值函数和移动赋值函数
- 不能包含虚函数和虚基类
代码示例
#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 main(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; // 平凡的类
system("pause");
return 0;
}
什么是标准布局的类型
- 所有非静态成员具有相同的访问权限
- 在继承树中最多只有一个类中有非静态数据成员
- 子类中的第一个非静态成员的类型与其基类不同
因为如果相同,C++会为基类分配1字节的空间,因为C++中规定相同类型的对象必须拥有不同的地址 - 没有虚函数和虚基类
- 所有非静态数据成员均符合标准布局类型,其基类也符合标准布局
代码示例
#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 _tmain(int argc, _TCHAR* argv[])
{
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不符合标准布局类型
system("pause");
return 0;
}
POD类型的使用方法
下面一个一个复制POD类型为例的代码
#include <iostream>
#include <Windows.h>
using namespace std;
class A
{
public:
int x;
double y;
};
int _tmain(int argc, _TCHAR* argv[])
{
if (std::is_pod<A>::value)
{
std::cout << "before" << std::endl;
A a;
a.x = 8;
a.y = 10.5;
std::cout << a.x << std::endl;
std::cout << a.y << std::endl;
size_t size = sizeof(a);
char *p = new char[size];
memcpy(p, &a, size);
A *pA = (A*)p;
std::cout << "after" << std::endl;
std::cout << pA->x << std::endl;
std::cout << pA->y << std::endl;
delete p;
}
system("pause");
return 0;
}
POD类型的好处
- 字节赋值。这样就可以使用C语言中的memset和memcpy对POD类型进行初始化
- 提供C对的内存布局的兼容。因为POD类型的数据在C和C++间的操作都是安全的
- 保证静态初始化的安全。静态初始化可以提高性能,POD类型对象的初始化更加简单