0%

C++ POD的介绍

POD(plain old data)介绍

简旧类型(plain old data)

  • 一个标量类型(scalar type)
  • 简旧类型(POD)数组
  • 一个符合以下要求的class类型(class or struct or union)
    • C++11以前:
      • 是一个聚合类型(aggregate type)
      • 所有非静态成员都是简旧类型(POD)
      • 没有成员是引用类型
      • 没有用户定义的拷贝构造函数
      • 没有用户定义的析构函数
    • C++11以后
      • 是一个平凡类型(trivial type)
      • 是一个标准布局类型
      • 所有非静态成员是简旧类型(POD)

POD类型特别在哪里?

What are Aggregates and PODs and how/why are they special?

POD-classesPD-unions, scalar type数组这样的类型被统一的叫做POD-typesPODs在很多地方都非常特别。下面一些例子。

  • POD-classes最接近C语言形式的结构体。不同的是,PODs可以有成员函数和任意静态成员,但他们两者都不能改变对象的内存排布。所以假如你想要写一个或多或少可移植型的可以被C语言甚至.NET使用的动态库,你应该尝试你所有导出的函数和返回值都是POD-types.
  • 一个non-POD类类型对象的生存周期开始于当构造函数结束,结束于当析构函数结束。对于POD类型类,生命周期开始于内存空间被对象占用,结束于内存空间被释放或者被重用后。
  • 对于POD类型的对象, 标准保证它当你使用memcpy对你对象中内容转化为charunsigned数组时,然后memcpy这个内容回到你的对象内,这个对象将持有原始的值。请注意:对于non-POD类型对象没有这样的保证。下面的例子假设类型TPOD类型。
1
2
3
4
5
6
#define N sizeof(T)
char buf[N];
T obj; ///< obj initialized to its original value
memcpy(buf, &obj, N);
memcpy(&obj, buf, N);
/// 保持它的原始值
  • goto语句. 你可能知道,通过goto从一个一些变量还没有在这个作用域中定义的点跳转到一个已经定义的点是非法的(编译器会报错)。这个限制应用在只有当这个变量是一个non-POD类型。看下面例子中f()是语义错误, g()则符合语义。注意,微软编译器在这条规则上特别松散,它在这两个情况下只是抛出一个警告。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int f()
{
struct NonPOD {NonPOD(){}};
goto label;
NonPOD x;
label:
return 0;
}

int g()
{
struct POD{int i; char c;};
goto label;
POD x;
label:
return;
}
  • 它保证了在POD对象的开始处没有内存填充位。其他情况下,假如一个POD-class: A使一个类型T的第一个成员,你可以安全的使用reinterpret_castA*T*然后获取指向第一个成员的指针,反之亦然。

补充定义

标量类型(scalar type)

scalar type是一个不是数组类型或class类型的(可能constvolatile限定的[^2])object类型.
英文原文[^1]

scalar types are (possibly cv-qualified) object types that are not array types or class types

聚合类型(aggregate type)

首先介绍一下聚合类型:
聚合类型是以下类型的其中一种[^3]:

  • 数组类型
  • class类型(典型的例子, struct, union):
    • 没有privateprotected非静态数据成员(到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没有虚函数和虚基类函数。

参考文献和扩展阅读

[^1]:What is a scalar Object in C++?

[^2]:What does “cv-unqualified” mean in C++?, cv (const and volatile) type qualifiers

[^3]:C++ standard: aggregate type

[^4]:C++ standard: C++ named requirements: TrivialType

[^5]: C++ standard: C++ named requirements: TriviallyCopyable