背景:
几乎所有的系统底层都是用 C 写的,当时定义的基本数据类型有 int、char、float 等整数类型、浮点类型、枚举、void、指针、数组、结构等等,编译器都可以正确的把它解析出来。到了 C++ 诞生之后,出现了继承、派生这样新的概念,于是就诞生了一些新的数据结构。比如某个派生类,C 语言中哪有派生的概念啊,遇到这种数据编译器就不认识了。为了和旧的 C 数据相兼容,C++ 就提出了 POD 数据结构概念。
POD 是 Plain Old Data 的缩写,Plain 代表它是一个普通类型,Old 代表它是旧的。
能用 C 的 memcpy() 等函数进行操作的类、结构体就是 POD 类型的数据。
对于任何POD对象,我们都可以放心大胆地使用memset()、memcpy()、memcmp()等函数对对象的内存数据进行操作。
由于非POD对象的存在,在C++中使用memcpy()系列函数时要保持足够的小心。为什么C++中的对象有可能不是一个POD呢?这还要从C++的重要特征之一—动多态说起。动多态的一个基本支撑技术就是虚函数。
目的在于:基本上谈到这个概念,一般都是说某某 class、struct、union 是不是 POD 类型的。
那什么样的类、结构体是拥有 POD 特性的呢?
要求有两个:
1.一个是它必须很平凡、很普通;
2另一个是布局有序。
能平凡就平凡
1.拥有默认的构造函数和析构函数。不能自定义写 构造/析构函数、拷贝/移动构造函数、拷贝/移动运算符,而是用编译器自动为我们生成,那这个数据就是“平凡的”。非要写的话,用 C++ 11 的 default 关键字。
class MyClass
{
public:
MyClass() = default;
MyClass(int a) :m_i(a) {
};
~MyClass();
private:
int m_i;
};
MyClass::MyClass()
{
}
MyClass::~MyClass()
{
}
一旦定义了构造函数,即使构造函数不包含参数、函数体也没有任何代码,该构造函数也不再平凡。
2.不能有 虚函数 和 虚基类。
解释:虚基类是指在多次继承中通过虚继承的基类。
布局要有序
前提:我们把非静态数据称为普通数据。
1.普通成员有相同的访问级别(private、public、protected)。
延伸:struct默认共有,class默认私有
2.第一个成员必须是自己的。即类的第一个非静态成员的类型与基类不同。
3.在类或者结构体继承时,普通成员只能在基类或者派生类其中一个类中,不可分散.要么在基类,要么在派生类。
pod的好处
1.字节赋值,可以安全使用memset和memcpy对pod类型进行初始化和拷贝操作。
2.提供对c内存布局兼容。
3.保证静态初始化的安全有效。
延伸:
但如果结构体或类是含有散列容器等嵌套的复杂结构体,使用memset进行初始化容易出错。
typedef struct tagDataInfo
{
long offs; // 索引
char name[20]; // 姓名
std::list<int> ord_list; // 定单索引列表
std::map<std::string, std::string> str_map; // 编号对应管理
tagDataInfo() { memset(this, 0, sizeof(*this)); };
} DataInfo;
程序执行的时候,程序core dump了。
原因是:std::list 和std::map 都是散列容器,存储的内存不是连续的。如果是std::vector就不会报错。
应该是
typedef struct tagDataInfo
{
long offs; // 索引
char name[20]; // 姓名
std::list<int> ord_list; // 定单索引列表
std::map<std::string, std::string> str_map; // 编号对应管理
tagDataInfo()
{
offs = 0;
memset(name, '0', sizeof(char) * 20);
ord_list.clear();
str_map.clear();
};
} DataInfo;
说明了不要随便对结构体和类调用memset、memcpy函数。