引子

在介绍C++11的文章或者博客中,经常会出现POD类型和Trivial类型的影子。但是POD类型和Trivial类型到底是什么意思呢?

  • POD类型
  • POD类型的好处

POD类型

粗略上来讲,POD是C++为兼容C的内存布局而设计的,主要用于修饰用户自定义类型。但POD却远比这个要复杂。POD(Plain Old Data),从字面意思上来看,是平凡普通的老旧格式的数据,POD是一个类型属性,既不是关键字也不会像”volatile”用来修饰类型信息。

POD类型,说明该数据是普通的,不会有什么虚函数啊,虚继承啊,或者内嵌的数据类型很复杂的情况。也就是说,POD类型的C++变量,可以直接用C语言中的struct来解释。

如下代码所示:

class PODType
{public:int mem_int;char* mem_cp;
};

PODType类型的内存布局如下的,POD类型太过于普通以至于使用简简单单的C语言结构体即可表示:

如果类型含有虚函数将会如何呢?比如:

class NonPODType
{public:int mem_int;// 含有虚函数,内存布局中有虚表指针virtual int func() { return 0; }
};

NonPODType类型的内存布局如下所示,C语言内存布局中哪里来的虚表指针呢,C语言根本就无法解释这种情况:

直观的来书,POD类型中的O代表了与C语言的兼容性,可以直接使用memcpy()直接复制而不会出现任何问题。POD这个想法的由来就是要支持两种最基本的属性:
(1)支持静态初始化
(2)编译C++中的POD类型所得到的内存布局,和C中编译struct的内存布局相同

具体地,C++11将POD类型划分为两个概念:

  • trivial
  • standard layout

在C++11中,更多的是用trivial和standard类代替POD类型。POD类型的递归定义如下:

A POD struct is a non-union class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types).

Similarly, a POD union is a union that is both a trivial class and a standard layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types).

A POD class is a class that is either a POD struct or a POD union.

Trivial平凡类型

平凡类型的标准定义如下:

A trivially copyable class is a class that:
— has no non-trivial copy constructors (12.8),
— has no non-trivial move constructors (12.8),
— has no non-trivial copy assignment operators (13.5.3, 12.8),
— has no non-trivial move assignment operators (13.5.3, 12.8), and
— has a trivial destructor (12.4).
A trivial class is a class that has a trivial default constructor (12.1) and is trivially copyable.
[ Note: In particular, a trivially copyable or trivial class does not have virtual functions or virtual base classes.—end note ]

下面我们依次解释上面的定义,
(1)拥有平凡的构造函数(trivial constructor)和析构函数(trivial destructor)
。平凡的默认构造函数即构造函数“什么都不干”。通常情况下,不定义类的构造函数,编译器就会为我们生成一个平凡的默认构造函数。注意哦,是通常情况下,如果该类含有虚函数或者数据成员没有默认构造函数,编译器是不会为我们生成平凡的默认构造函数的。

注意,如果你定义了构造函数,即使构造函数体为空,则该类型也是不平凡的。仔细想一想,也对,既然POD类型与C语言结构体等价,C语言哪来的用户定义构造函数呢。当然可以使用default关键字强制使用编译器提供的默认的平凡构造函数。

(2)拥有平凡的拷贝构造函数(trivial copy constructor)和移动构造函数(trivial copy constructor)。平凡的拷贝构造函数基本上等同于memcpy进行类型的构造。同平凡构造函数一样,编译器在用户不提供的情况下,通常会提供平凡的拷贝构造函数。

平凡的移动构造函数执行的操作与平凡的拷贝构造函数相同,都是bytewise的拷贝,只是用于移动语义。移动构造函数嘛,只是提供了一个接口来捕获右值,右值在C++11中分为两种,将忘值(xvalue, expiring value)纯右值(prvalue, Pure Rvalue)。将忘值包括返回T&&的函数返回值,std::move()返回值以及返回T&&的类型转换函数的返回值。纯右值基本等同于临时变量(对象)。

(3)拥有平凡的拷贝赋值运算符(trivial assignment operator)和移动赋值运算符(move operator)。

(4)不能包含虚函数以及虚基类。

C++11提供了内置函数来支持Trivial类型的判断。

template <typename T> struct std::is_trivial

Standard Layout标准布局

POD包含的另外一个概念就是标准布局。标准布局的类或结构体应该符合以下定义:
(1)所有的非静态成员有相同的访问权限。成员变量同为public,private,protected。

首先静态成员不参与对象的内存布局,而是存放在全局数据区。

相同访问权限,也可以理解,C语言中struct数据成员默认都是public,数据成员的访问权限都相同,不存在相邻的数据成员在类型以外的任何不同。

(2)在类或者结构体继承时,满足一下两种情况之一:

  • 派生类中有非静态成员,且只有一个仅包含静态成员的基类。
  • 基类有非静态成员,而派生类中没有非静态成员
  • 派生类有非静态成员,而基类中没有静态成员(但是非静态成员的类型必须与基类不同

只要非静态成员同时出现在派生类中或者基类中,都不属于标准布局。而多重继承也会导致类型布局的一些变化,所以一旦非静态成员出现在多个基类中,派生类也不属于标准布局的。

(3)类中第一非静态成员的类型与其基类不同

class A {};
class B : public A
{
public: A a;int mem;
};

B类型就不是标准布局的。这个特殊要求要从C++中的一个标准说起,同一类型的两个对象的地址必须不同

class Non {};
int main()
{Non n1;Non n2;int num = 10;// Non类型的大小为1cout << sizeof(Non) << endl;return num;
}

上述代码中的n1和n2属于同一类型,并且该类型是空的。如果编译器不对该类型的内存做特殊处理,那么n1和n2的地址是相同,这当然是不合理的。所以,编译器给空的class类型都安插了一个隐含的字节。当然,有时候编译器会padding出3个字节以便对齐。main的栈帧如下图所示:

C++标准允许,在基类没有成员时,派生类第一个成员与基类共享地址。但是当派生类中第一个数据成员类型为基类类型时,有趣的问题就来了。首先,这时派生类的内存布局包括基类部分的内存布局,同时自己又添加了另外一个基类类型的变量,如果编译器优化实现第一个成员和基类部分共享地址,那么就违背了C++标准的另一个要求,同类型的不同对象地址必须不同。

所以在标准布局的解释中,C++11标准强制要求派生类的第一个非静态成员的类型必须与基类不同。比如:

class A{};
class B : public A
{public: A a;int mem;
};
int main()
{ B b;return 0;
}

main的栈帧如下图所示:

(4)没有虚函数和虚基类
(5)所有非静态数据成员均符合标准布局类型,其基类也符合标准布局,这是一个递归定义。注意,静态数据成员是存放在全局数据区的,不参与内存布局。


POD类型的好处

POD类型多用于类和结构体。POD带来的好处如下:

  • 字节赋值(bytewise copy)。可以使用memset和memcpy对POD类型进行初始化

  • 提供对C内存布局的兼容。C++程序可以与C函数进行交互操作,POD类型保证这种在C与C++之间的操作总是安全的

  • 保证了静态初始化的安全有效,用于提供程序性能。直接放入.bss段,在初始化中直接赋0

尾注:本博客中的内容借鉴摘抄了《深入理解C++11》中的部分内容

C++11中的POD和Trivial相关推荐

  1. C++ 11 中的POD

    POD 是英文中Plain Old Data 的缩写,意如其名. Plain 表示了POD 是普通的类型, C++中常见的类型都是这样的属性,而不像一些存在着虚函数虚继承的类型那么特别. Old 则体 ...

  2. 【C/C++ POD 类型】深度解析C++中的POD类型:从理论基础到项目实践

    深度解析C++中的POD类型:从理论基础到项目实践 1. C++中的POD类型(Plain Old Data) 1.1 POD类型的定义和特性 Trivial类型 Standard layout类型 ...

  3. C++11中头文件type_traits介绍

    C++11中的头文件type_traits定义了一系列模板类,在编译期获得某一参数.某一变量.某一个类等等类型信息,主要做静态检查. 此头文件包含三部分: (1).Helper类:帮助创建编译时常量的 ...

  4. C ++中的POD类型是什么?

    我有几次遇到这个术语POD型. 这是什么意思? #1楼 POD代表普通旧数据 - 即没有构造函数,析构函数和虚拟成员函数的类(无论是使用关键字struct还是关键字class ). 维基百科关于POD ...

  5. C++11 中值得关注的几大变化

    2019独角兽企业重金招聘Python工程师标准>>> 源文章来自前C++标准委员会的 Danny Kalev 的 The Biggest Changes in C++11 (and ...

  6. 概率论中指数分布介绍及C++11中std::exponential_distribution的使用

    指数分布:在深度学习中,我们经常会需要一个在x=0点处取得边界点(sharp point)的分布.为了实现这一目的,我们可以使用指数分布(exponential distribution): p(x; ...

  7. 概率论中高斯分布(正态分布)介绍及C++11中std::normal_distribution的使用

    高斯分布:最常用的分布是正态分布(normal distribution),也称为高斯分布(Gaussian distribution): 正态分布N(x;μ,σ2)呈现经典的"钟形曲线&q ...

  8. C++11中值得关注的几大变化 .

    Lambda 表达式 Lambda 表达式的形式是这样的: view plaincopy to clipboardprint? [capture](parameters)->return-typ ...

  9. C++11中值得关注的几大变化

    赖勇浩(http://laiyonghao.com) 声明:本文源自 Danny Kalev 在 2011 年 6 月 21 日发表的<The Biggest Changes in C++11( ...

最新文章

  1. 微软亚洲研究院周明:从语言智能到代码智能
  2. 让Tee 7.x版本和FastReport 3.x版本共存
  3. 开发工具:IDEA 调试技巧,非常实用,欢迎收藏!
  4. linux显示系统信息软件下载,linux查看系统信息软件安装信息命令学习笔记
  5. Android官方开发文档Training系列课程中文版:管理Activity的生命周期之暂停和恢复Activity
  6. mysql sql组合_详解mysql 组合查询
  7. 徐州医科大学计算机报名,2019年徐州医科大学计算机等级考试准考证打印
  8. 【コンテンツ配信高速化 】
  9. 百度AI语音语义一体化技术 识别的同时进行语义分析
  10. 黑苹果 U盘刻录工具Transmac与Etcher使用
  11. 软件工程导论——软件工程介绍
  12. android 系统
  13. 关于COM类工厂80070005和8000401a错误分析及解决办法(DCOM)
  14. IIS6.0+Tomcat整合(java,C++等教程免费下载)
  15. 2022年C等级考试九月二级真题E:反反复复
  16. mysql里guest用户_MySQL降权:MySQL以Guests帐户启动设置方法
  17. devc++不兼容_最好的兼容HomeKit的智能灯泡
  18. 2006求奇数的乘积c++
  19. 杨振宁李政道分手内幕
  20. 场效应管(FET)总结:

热门文章

  1. hibernate中日期条件查询问题setDate,setTimestamp
  2. springboot 项目中,如何在jsp页面获取contextPath
  3. 摄像头录像以及回放工具
  4. Linux操作系统-标准IO库(1)
  5. 基于Java+uniapp微信小程序的购物商城系统设计与实现
  6. 第三十二章 三更雪压飞狐城(一之全)
  7. 第一章,安装spark集群,测试和运行wordcount 案例
  8. JVM命令行监控工具之jmap(JVM Memory Map)
  9. gorm多表联合查询
  10. S5PV210 ADC驱动分析