0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

POD到底是什么?聊聊POD

CPP开发者 来源:高性能架构探索 2023-06-19 15:24 次阅读

概述

POD,即Plain Old Data的缩写,plain代表普通,Old代表旧,从字面意思看是老的、普通的数据类型。这个概念由C++引入主要是为了与C兼容,或者说POD就是与C兼容的那边部分数据类型。在C++对POD类型进行序列化生成二进制后,在C语言中可以对该二进制进行解析成功。如果对于一个非POD类型,假如包含虚函数的class,大家知道编译器在操作的时候会加入虚函数指针,但是虚函数这个概念在C语言中不存在,遇到这种数据编译器就不认识了,或者说对于一个非POD类型的数据,C语言是不识别的,于是C++就提出了POD数据类型的概念。

POD的一种常见用法是跨系统或者跨语言进行通讯,比如与C/.NET等编写的代码进行通讯。

概念

structA{
intx;
inty;
};
structB{
private:
intx;
public:
inty;
};
structC{
inta;
intb;
C(intx,inty):a{x},b{y}{}
};

那么,问题来了,上述三个类型A、B和C,哪个是POD类型?

如果我们不清楚POD的判断标准的话,只能靠猜来回答该问题,幸运的是Modern Cpp提供了接口来进行判断(PS:下面的trivial和standard layout用来辅助判断):

C++11 C++17 描述
std::is_pod std::is_pod_v 通过其value是否为ture来表示是否为POD类型(std::is_pod::value)
std::is_trivial std::is_trivial_v 通过其value是否为ture来表示是否为平凡类型(std::is_trivial::value)
std::is_standard_layout std::is_standard_layout_v 通过其value是否为ture来表示是否为标准布局(std::is_standard_layout::value)

如果使用上述接口对前面例子中的对象A、B和C进行判断的话,结果如下:

类型 Trivial(平凡类型) Standard layout(标准布局) POD
A
B
C

从上述结果可以看出,B是平凡类型,C是标准布局,A既是平凡类型,又是标准布局,同时也是POD类型,这就引出了POD的定义:

A POD type is a type that is both trivial and standard-layout. This definition must hold recursively for all its non-static data members.

通过上述定义可以看出,POD类型既是平凡类型又是标准布局,反过来可以理解为如果一个类型既是平凡类型又是标准布局,且其内部非静态成员变量也满足该条件(既是平凡类型又是标准布局),那么这个类型就是POD类型。

标准对POD定义如下:

A POD class is a class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD class (or array thereof). A POD type is a scalar type, a POD class, an array of such a type, or a cv-qualified version of one of these types.

与前一个定义相比,新增了一个类型scalar type,cppference中提到,scalar type为以下几个之一:

•an arithmetic type

•an enumeration type

•a pointer type

•a pointer-to-member type

•thestd::nullptr_ttype

•cv-qualified versions of the above types

好了,从上面的内容中提到,一个POD类型的类,其非静态成员变量也必须是POD的,对静态成员变量和成员函数则没有这个要求,如下这个类D,其仍然是POD:

structD{
inta;
intb;
staticstd::strings;
intget(){
returna;
}
};

在本小节中,我们提到了三个概念:Trivial(平凡类型)、Standard layout(标准布局)以及Scalar type,对于最后一个Scalar type比较简单,所以在后面的内容中,将针对前两个概念进行详细分析。

Trivial

这个概念比较抽象,乃至于很难用一句简单的话来描述。于是查阅了cppreference,显示标准对这块也没有一个完整的定义:

Note: the standard doesn't define a named requirement with this name. This is a type category defined by the core language. It is included here as a named requirement only for consistency.

于是搜索了相关资料,微软官网对这块的定义如下:

When a class or struct in C++ has compiler-provided or explicitly defaulted special member functions, then it is a trivial type. It occupies a contiguous memory area. It can have members with different access specifiers. In C++, the compiler is free to choose how to order members in this situation.

也就是说,当一个类型(class/struct)同时满足以下几个条件时,它就是trivial type:

•没有虚函数或虚基类。

•由编译器生成(使用=default或者=delete)默认的特殊成员函数,包括默认构造函数、拷贝构造函数、移动构造函数、赋值运算符、移动赋值运算符和析构函数。

•数据成员同样需要满足条件 1 和 2。

举例如下:

classA{
public:
A(inti):n(i){}
A(){}
private:
intn;
};

classB{
public:
B(inti):n(i){}
B()=default;
private:
intn;
};

classC{
public:
C(inti):n(i){}
C()=delete;
private:
intn;
};

structBase{
intx;
inty;
};

structDerived:publicBase
{
private:
intz;
};

intmain(){
std::cout<< std::is_trivial::value<< std::endl; // print false
  std::cout << std::is_trivial::value<< std::endl; // print true
  std::cout << std::is_trivial::value<< std::endl; // print true
  std::cout << std::is_trivial::value<< std::endl; // print true
  std::cout << std::is_trivial::value<< std::endl; // print true
  
  return 0;
}

平凡类型具有如下属性:

•占据一块连续的内存区域

•由于对齐要求,成员变量之间可以填充对齐字节(padding)

•可以使用 memcpy进行对象拷贝

•可以将一个平凡的类型通过memcpy()放入char或者unsigned char数组,然后可以把数组内的内容重新组装成一个该类型对象

•允许有多个不同的访问控制符,但是,在这种情况下,编译器有可能对其进行重排

针对上述最后一个属性,示例如下:

structC{
public:
intx;

private:
inty;

public:
intz;
};

编译器可以将其重新排序为如下这种:

structC{
public:
intx;
intz;
private:
inty;
};

正是因为如上原因(访问权限编译器重排),普通类型不能安全的与其他语言编写的代码进行交互操作。我们以C语言为例,编译器的重排导致不能不能与C语言兼容(或者说C语言解析失败),因为C语言不识别private这个访问权限,如果进行交互操作,可能会导致其他意想不到的问题。

Standard layout

布局指的是类、结构体或者联合(Union)的成员在内存中的排列。标准布局定义了这样一种类型,它不使用C中不存在的而在CPP中存在的某些功能或者特性。如果某个类是标准布局,那么可以通过memcpy进行复制,而且可以与C语言中定义的同种类型进行交互。一言以蔽之,具有标准布局类的类或者结构体等与C兼容,并行可以通过C的API进行交互。

既然符合标准布局的类只具有C语言中存在的功能或者特性,那么,很容易总结出来标准布局的条件:

1.没有虚函数或者虚基类

2.没有引用类型的非静态成员变量

3.所有的非静态成员变量具有相同的访问控制权限

4.所有的非静态成员变量和基类都是标准布局

5.没有多重继承导致的菱形问题

6.子类中的第一个非静态成员的类型与其基类不同

7.在class或者struct继承时,满足以下两种情况之一(总结就是要么子类有非静态成员变量,要么父类有):

•派生类中有非静态成员,且只有一个仅包含静态成员的基类

•基类有非静态成员,而派生类没有非静态成员

现在我们结合示例代码进行分析:

structA{
};
structB{
Aa;
doubleb;
};
structC{
voidfoo(){}
};
structD:publicC
{
intx;
inty;
};

依据前面标准布局的要求,上述几个类A、B、C和D都是标准布局。现在我们构造稍微复杂点的例子:

structE
{
intx;
};

structF:publicE
{
inty;
};

structG
{
intx;
private:
virtualvoidfoo(){};
};

structH{};
structX:publicH{};
structY:publicH{};
structK:publicX,Y{};

structL{
intx;
private:
inty;
};

上面这些例子中,E是标准布局,G不属于标准布局(虚函数,不满足条件1),K不属于标准布局(菱形继承,不满足条件5),L不属于标准布局(不同的访问权限,不满足条件3)

接着我们看下前面条件中比较难理解的一个子类中的第一个非静态成员的类型与其基类不同,示例如下:

structM{
intx;
};

structN:publicM{
Mm;
inta;
};

structX{
intx;
};

structY:publicX{

};

structZ:publicX{
inty;
};

在上述例子中,M、X和Y是标准布局,而N(不满足条件6)和Z(不满足条件7)不是标准类型。


审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编译器
    +关注

    关注

    1

    文章

    1577

    浏览量

    48638
  • C++语言
    +关注

    关注

    0

    文章

    146

    浏览量

    6878
  • POD
    POD
    +关注

    关注

    0

    文章

    16

    浏览量

    5973

原文标题:我们通常说的 POD 到底是什么?

文章出处:【微信号:CPP开发者,微信公众号:CPP开发者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    介绍如何安装E1135C PDU和Pod升级

    Describes how to install the E1135C PDU and Pod upgrade.
    发表于 08-21 14:07

    从零开始入门 K8s| 详解 Pod 及容器设计模式

    LogCollector 容器给看到。以上就是 Pod 实现存储的方式。三、详解容器设计模式现在我们知道了为什么需要 Pod,也了解了 Pod 这个东西到底是怎么实现的。最后,以此为
    发表于 09-20 15:12

    Pod资源配置

    《Kubernetes进阶实战》第四章《管理Pod资源对象》
    发表于 10-22 14:39

    寻找Kintex UltraScale POD12输出Vol / Voh?

    我正在寻找Kintex UltraScale POD12输出Vol / Voh。 DS892表16显示了直流输出电平,但这些值似乎仅用于测量Rol / Roh。我应该使用表9中的LVCMOS12
    发表于 07-27 14:52

    Land Pattern and POD

    Land Pattern and POD
    发表于 03-05 15:59 0次下载
    Land Pattern and <b class='flag-5'>POD</b>

    Kubernetes组件pod核心原理

    1. 核心组件原理 —— pod 核心原理 1.1 pod 是什么 pod 也可以理解是一个容器,装的是 docker 创建的容器,也就是用来封装容器的一个容器; pod 是一个虚拟化
    的头像 发表于 09-02 09:27 1585次阅读

    pod底层网络和数据存储是如何进行的

    1. 核心组件原理 —— pod 核心原理 1.1 pod 是什么 pod 也可以理解是一个容器,装的是 docker 创建的容器,也就是用来封装容器的一个容器; pod 是一个虚拟化
    的头像 发表于 09-24 11:35 1474次阅读

    如何利用Docker实现Pod

    Container 和 Pod 是相似的。在底层,它们主要依赖 Linux 命名空间和 cgroup。但是,Pod 不仅仅是一组容器。Pod 是一个自给自足的高级构造。
    发表于 11-14 12:51 992次阅读

    Kubernetes中的Pod简易理解

    Pod是Kubernetes中非常重要的概念,也是Kubernetes管理的基本单位。正如其名,Pod像一个豌豆荚,可以容纳多个container,拥有相同的IP地址。
    的头像 发表于 02-15 10:44 934次阅读

    什么是CNI,基于Calico的Pod网络介绍

    每一个Node上都会有一个tunl0的虚拟网卡,这个网卡可以理解成网桥,所有Pod都要基于此网桥来和其它Pod通信。 ② 每生成一个新的Pod,那么在Node上都会生成一个calixxxx的虚拟网卡,这个网卡会对应到
    发表于 04-20 09:37 1716次阅读

    k8s与pod之间是如何进行网络隔离的?

    NetworkPolicy用来控制PodPod之间的网络通信,它也支持针对Namespace进行限制。
    的头像 发表于 05-11 09:35 1187次阅读
    k8s与<b class='flag-5'>pod</b>之间是如何进行网络隔离的?

    iOS中Pod库资源引用探究

    Cocoapods中Pod引用资源的方式有多种,不同的方式对资源的使用还是有区别的,但也有一定的规律,这里我用一个样例工程来进行说明,样例工程名叫:AssetsDemo,使用pod lib create AssetsDemo命令创建,目录结构如图:
    的头像 发表于 05-26 11:11 577次阅读
    iOS中<b class='flag-5'>Pod</b>库资源引用探究

    Kubernetes Pod如何获取IP地址呢?

    Kubernetes 网络模型的核心要求之一是每个 Pod 都拥有自己的 IP 地址并可以使用该 IP 地址进行通信。很多人刚开始使用 Kubernetes 时,还不清楚如何为每个 Pod 分配 IP 地址。
    的头像 发表于 07-21 10:00 543次阅读
    Kubernetes <b class='flag-5'>Pod</b>如何获取IP地址呢?

    Pod是如何在底层实现的?如何使用Docker创建Pod

    刚开始接触 Kubernetes 时,你学到的第一件事就是每个 Pod 都有一个唯一的 IP 和主机名,并且在同一个 Pod 中,容器可以通过 localhost 相互通信。所以,显而易见,一个 Pod 就像一个微型的服务器。
    的头像 发表于 08-14 10:33 841次阅读
    <b class='flag-5'>Pod</b>是如何在底层实现的?如何使用Docker创建<b class='flag-5'>Pod</b>?

    Pod一直处于Pending状态?什么是Pod拓扑约束?

    起因: 今天在部署组件的时候,发现组件的pod一直处于Pending状态,报错显示的原因是:不满足Pod拓扑分布约束,看了代码发现是原来同事给组件新增了Pod拓扑约束。
    的头像 发表于 12-18 11:46 698次阅读
    <b class='flag-5'>Pod</b>一直处于Pending状态?什么是<b class='flag-5'>Pod</b>拓扑约束?