概述
很久很久以前,c 语言统一了江湖。几乎所有的系统底层都是用 c 写的,当时定义的基本数据类型有 int、char、float 等整数类型、浮点类型、枚举、void、指针、数组、结构等等。然后只要碰到一串 01010110010 之类的数据,编译器都可以正确的把它解析出来。
那么到了 c 诞生之后,出现了继承、派生这样新的概念,于是就诞生了一些新的数据结构。比如某个派生类,c 语言中哪有派生的概念啊,遇到这种数据编译器就不认识了。可是我们的计算机世界里,主要的系统还是用 c 写的啊,为了和旧的 c 数据相兼容,c 就提出了 pod 数据结构概念。
pod 是 plain old data 的缩写,是 c 定义的一类数据结构概念,比如 int、float 等都是 pod 类型的。plain 代表它是一个普通类型,old 代表它是旧的,与几十年前的 c 语言兼容,那么就意味着可以使用 memcpy() 这种最原始的函数进行操作。两个系统进行交换数据,如果没有办法对数据进行语义检查和解释,那就只能以非常底层的数据形式进行交互,而拥有 pod 特征的类或者结构体通过二进制拷贝后依然能保持数据结构不变。也就是说,能用 c 的 memcpy() 等函数进行操作的类、结构体就是 pod 类型的数据。
基本上谈到这个概念,一般都是说某某 class、struct、union 是不是 pod 类型的。
pod 的特征
是不是 pod 类型的,可以用 is_pod
能平凡就平凡
trival(平凡)是个概念,我也暂时找不到特别合适的翻译,满足以下条件即可:
不能写 构造/析构函数、拷贝/移动构造函数、拷贝/移动运算符,而是用编译器自动为我们生成,那这个数据就是“平凡的”。非要写的话,用 c 11 的 default 关键字。例如下列代码用 std::is_trivial
你看,a 类手写了个构造函数,虽然什么都没填,但这构造函数已经不是编译器默认提供的了,所以不平凡,所以就不是 pod 类,自然就不能用诸如 memcpy() 这种 c 语言的函数来操作;b 类的一堆构造函数啥的都没写,默认由编译器提供,所以是平凡的;c 类虽然写了构造函数,但用了 c 11 的 default 关键字修饰,也是平凡的。
不能有 虚函数 和 虚基类。只要满足以上条件,就是拥有平凡特征的数据类型。
布局要有序
除了平凡之外,还对布局有要求。为了便于理解讲述,我们把非静态数据称为普通数据。
普通成员有相同的访问级别。例如下面的类,因为 a 和 b 的访问级别不一样,所以布局无序,自然就不是 pod 类型的数据。当然,如果 b 写成 static int b,例子中的 a 类就是 pod 类型的了。所以一定要看清每个字,是“普通成员”哦。
第一个成员必须是自己的。
只要有父类,普通成员只能在其中一个类中,不可分散。因为 c 没有继承的概念,所以就别把普通成员在两个类中都写,写在一个类中就像 c 的风格了。如下图的代码,从 a 的角度看上边没有父类,就按上文的规则去判断是否是 pod 类型。从 a1 的角度看上边有个父类,这个时候就要看父子两个是否都有普通成员了,都有的话肯定不行,只能其中一个有。
如何使用 pod 类型的数据?
前面既然说了,具有 pod 性质的数据可以使用 c 语言中的 memcpy() 等底层函数,那我们来看看怎么用。