本文详细介绍了C++中的POD(Plain Old Data)类型,解释了其定义、特性以及在编程中的特殊用途。POD类型包括标量类型、POD数组和符合特定条件的类类型,具有内存布局明确、生命周期简单等特点。文章还探讨了POD类型在内存操作(如memcpy)、goto语句和类型转换中的优势,并提供了相关标准定义的参考链接,帮助读者深入理解POD类型的重要性和应用场景。
POD(plain old data)介绍
简旧类型(plain old data)
- 一个标量类型(
scalar type) - 简旧类型(POD)数组
- 一个符合以下要求的
class类型(classorstructorunion)- C++11以前:
- 是一个聚合类型(
aggregate type) - 所有非静态成员都是简旧类型(
POD) - 没有成员是引用类型
- 没有用户定义的拷贝构造函数
- 没有用户定义的析构函数
- 是一个聚合类型(
- C++11以后
- 是一个平凡类型(
trivial type) - 是一个标准布局类型
- 所有非静态成员是简旧类型(
POD)
- 是一个平凡类型(
- C++11以前:
POD类型特别在哪里?
What are Aggregates and PODs and how/why are they special?
像POD-classes,PD-unions, scalar type和数组这样的类型被统一的叫做POD-types, PODs在很多地方都非常特别。下面一些例子。
POD-classes最接近C语言形式的结构体。不同的是,PODs可以有成员函数和任意静态成员,但他们两者都不能改变对象的内存排布。所以假如你想要写一个或多或少可移植型的可以被C语言甚至.NET使用的动态库,你应该尝试你所有导出的函数和返回值都是POD-types.- 一个
non-POD类类型对象的生存周期开始于当构造函数结束,结束于当析构函数结束。对于POD类型类,生命周期开始于内存空间被对象占用,结束于内存空间被释放或者被重用后。 - 对于
POD类型的对象, 标准保证它当你使用memcpy对你对象中内容转化为char或unsigned数组时,然后memcpy这个内容回到你的对象内,这个对象将持有原始的值。请注意:对于non-POD类型对象没有这样的保证。下面的例子假设类型T是POD类型。
1 |
|
goto语句. 你可能知道,通过goto从一个一些变量还没有在这个作用域中定义的点跳转到一个已经定义的点是非法的(编译器会报错)。这个限制应用在只有当这个变量是一个non-POD类型。看下面例子中f()是语义错误,g()则符合语义。注意,微软编译器在这条规则上特别松散,它在这两个情况下只是抛出一个警告。
1 | int f() |
- 它保证了在
POD对象的开始处没有内存填充位。其他情况下,假如一个POD-class: A使一个类型T的第一个成员,你可以安全的使用reinterpret_cast从A*到T*然后获取指向第一个成员的指针,反之亦然。
补充定义
标量类型(scalar type)
scalar type是一个不是数组类型或class类型的(可能const或volatile限定的[1])object类型.
英文原文[2]
scalar types are (possibly cv-qualified) object types that are not array types or class types
聚合类型(aggregate type)
首先介绍一下聚合类型:
聚合类型是以下类型的其中一种[3]:
- 数组类型
class类型(典型的例子,struct,union):- 没有
private和protected非静态数据成员(到C++11) - 没有用户定义的构造函数(显式的默认或删除的构造函数) (C++11起, 到C++17)
- 没有用户提供的继承的或显式的构造函数(显式的默认或删除的构造函数)(C++17起,到C++20)
- 没有用户定义的或继承的构造函数(C++20起)
- 没有基类(C++17之前), 没有
virtual,private,protected基类(C++17起) - 没有虚成员函数
- 没有默认成员的初始化器(从C++11到C++14)
- 没有
平凡类型 (TrivialType)
要求[4]:
- 可平凡复制(
TrivialCopyable) - 若该类型是类类型或其数组,则该类拥有一个或多个合格的默认构造函数,均为平凡的
可平凡可复制(Trivially Copyable)
下面列举的类型称作平凡可复制类型[5]:
- 标量类型
- 平凡可复制的类
- 至少有一个拷贝构造函数,移动构造函数,拷贝赋值符是符合要求的
- 每个合格的拷贝构造函数(假如有的话)是平凡的
- 每个合格的移动构造函数(假如有的话)是平凡的
- 每个合格的拷贝赋值符(假如有的话)是平凡的
- 每个合格的移动赋值符(假如有的话)是平凡的
- 有一个平凡的没有被删除的析构函数
- 可平凡复制的数组类型
这意味着一个平凡可拷贝的class没有虚函数和虚基类函数。