当前位置:网站首页>POD 类型
POD 类型
2022-06-22 14:47:00 【喜欢打篮球的普通人】
1.POD 类型
POD 是英文中 Plain Old Data 的缩写,翻译过来就是 普通的旧数据 。POD 在 C++ 中是非常重要的一个概念,通常用于说明一个类型的属性,尤其是用户自定义类型的属性。
POD 属性在 C++11 中往往又是构建其他 C++ 概念的基础,事实上,在 C++11 标准中,POD 出现的概率相当高。
- Plain :表示是个普通的类型
- Old :体现了其与 C 的兼容性,支持标准 C 函数
在 C++11 中将 POD 划分为两个基本概念的合集,即∶平凡的(trivial) 和标准布局的(standard layout ) 。
2.“平凡” 类型
一个平凡的类或者结构体应该符合以下几点要求:
(1)拥有平凡的默认构造函数(trivial constructor)和析构函数(trivial destructor)。
平凡的默认构造函数就是说构造函数什么都不干。
通常情况下,不定义类的构造函数,编译器就会为我们生成一个平凡的默认构造函数。
eg:
// 使用默认的构造函数
class Test {
};
一旦定义了构造函数,即使构造函数不包含参数,函数体里也没有任何的代码,那么该构造函数也不再是"平凡"的。
eg:关于析构函数也和上面列举的构造函数类似,一旦被定义就不平凡了。但是这也并非无药可救,使用 =default 关键字可以显式地声明默认的构造函数,从而使得类型恢复 “平凡化”。
class Test1
{
Test1(); // 程序猿定义的构造函数, 非默认构造
};
(2)拥有平凡的拷贝构造函数(trivial copy constructor)和移动构造函数(trivial move constructor)。
- 平凡的拷贝构造函数基本上等同于使用 memcpy 进行类型的构造。
- 同平凡的默认构造函数一样,不声明拷贝构造函数的话,编译器会帮程序员自动地生成。
- 可以显式地使用 = default 声明默认拷贝构造函数。
- 而平凡移动构造函数跟平凡的拷贝构造函数类似,只不过是用于移动语义。
(3)拥有平凡的拷贝赋值运算符(trivial assignment operator)和移动赋值运算符(trivial move operator)
- 这基本上与平凡的拷贝构造函数和平凡的移动构造运算符类似。
(4)不包含虚函数以及虚基类。
- 类中使用 virtual 关键字修饰的函数 叫做虚函数
class Base
{
public:
Base() {
}
virtual void print() {
}
};
- 虚基类是在创建子类的时候在继承的基类前加 virtual 关键字 修饰
class Base
{
public:
Base() {
}
virtual void print() {
}
};
- eg:
class Base
{
public:
Base() {
}
};
// 子类Child,虚基类:Base
class Child : virtual public Base
{
Child() {
}
};
3.“标准布局” 类型
标准布局类型主要主要指的是类或者结构体的结构或者组合方式。
标准布局类型的类应该符合以下五点定义,最重要的为前两条:
(1)所有非静态成员有相同 的访问权限(public,private,protected)。
- 类成员拥有不同的访问权限(非标准布局类型)
class Base
{
public:
Base() {
}
int a;
protected:
int b;
private:
int c;
};
- 类成员拥有相同的访问权限(标准布局类型)
class Base
{
public:
Base() {
}
int a;
int b;
int c;
};
(2)在类或者结构体继承时,满足以下两种情况之一∶
- 基类中包含静态成员(或基类没有变量),派生类中有非静态成员。
- 基类有非静态成员,而派生类没有非静态成员。
struct Base {
static int a;};
struct Child: public Base{
int b;}; // ok
struct Base1 {
int a;};
struct Child1: public Base1{
static int c;}; // ok
struct Child2:public Base, public Base1 {
static int d;); // ok
struct Child3:public Base1{
int d;}; // error
struct Child4:public Base1, public Child // error
{
static int num;
};
通过上述例子得到的结论:
- 非静态成员只要同时出现在派生类和基类间,即不属于标准布局。
- 对于多重继承,一旦非静态成员出现在多个基类中,即使派生类中没有非静态成员变量,派生类也不属于标准布局。
(3)子类中第一个非静态成员的类型与其基类不同。
- 此处基于 G++ 编译器讲解,如果使用 VS 的编译器和 G++ 编译器得到的结果是不一样的。
struct Parent{
};
struct Child : public Parent
{
Parent p; // 子类的第一个非静态成员
int foo;
};
- 上面的例子中 Child 不是一个标准布局类型**,因为它的第一个非静态成员变量 p 和父类的类型相同**,改成下面这样子类就变成了一个标准布局类型:
struct Parent{
};
struct Child1 : public Parent
{
int foo; // 子类的第一个非静态成员
Parent p;
};
这条规则对于我们来说是比较特别的,这样规定的目的主要是是节约内存,提高数据的读取效率。
- 对于上面的两个子类 Child 和 Child1 来说它们的内存结构是不一样的,在基类没有成员的情况下:
- C++ 标准允许标准布局类型(Child1)派生类的第一个成员foo与基类共享地址,此时基类并没有占据任何的实际空间(可以节省一点数据)
- 对于子类 Child 而言,如果子类的第一个成员仍然是基类类型,C++ 标准要求类型相同的对象它们的地址必须不同(基类地址不能和子类中的变量 p 类型相同),此时需要分配额外的地址空间将二者的地址错开。

(3)没有虚函数和虚基类。
(4)所有非静态数据成员均符合标准布局类型,其基类也符合标准布局,这是一个递归的定义。
4.对 “平凡” 类型判断
如果我们想要判断某个数据类型是不是属于 POD 类型,可以使用 C++11 给我们提供的相关函数:
对 “平凡” 类型判断
- C++11 提供的类模板叫做 is_trivial,其定义如下:
template <class T> struct std::is_trivial;
- std::is_trivial 的成员 value 可以用于判断 T 的类型是否是一个平凡的类型(value 函数返回值为布尔类型)。
- 除了类和结构体外,is_trivial 还可以对内置的标准类型数据(比如 int、float 都属于平凡类型)及数组类型(元素是平凡类型的数组总是平凡的)进行判断。
- eg:
#include <iostream>
#include <type_traits>
using namespace std;
class A {
};
class B {
B() {
} };
class C : B {
};
class D {
virtual void fn() {
} };
class E : virtual public A {
};
int main()
{
//boolalpha 是头文件#include <iostream> 中的 一个函数,是把bo ol 型变量按照true或false 的
cout << std::boolalpha;
cout << "is_trivial:" << std::endl;
//内置标准数据类型,属于 trivial 类型
cout << "int: " << is_trivial<int>::value << endl;
//拥有默认的构造和析构函数,属于 trivial 类型
cout << "A: " << is_trivial<A>::value << endl;
//自定义了构造函数,因此不属于 trivial 类型
cout << "B: " << is_trivial<B>::value << endl;
//基类中自定义了构造函数,因此不属于 trivial 类型
cout << "C: " << is_trivial<C>::value << endl;
//类成员函数中有虚函数,因此不属于 trivial 类型
cout << "D: " << is_trivial<D>::value << endl;
//继承关系中有虚基类,因此不属于 trivial 类型
cout << "E: " << is_trivial<E>::value << endl;
return 0;
}
- 测试:

5.对 “标准布局” 类型的判断
同样,在 C++11 中,我们可以使用模板类来帮助判断类型是否是一个标准布局的类型,
- 其定义如下:
template <typename T> struct std::is_standard_layout;
- 通过 is_standard_layout 模板类的成员 value(is_standard_layout∶∶value),我们可以在代码中打印出类型的标准布局属性,函数返回值为布尔类型。
- eg:
// pod.cpp
#include <iostream>
#include <type_traits>
using namespace std;
struct A {
};
struct B : A {
int j; };
struct C
{
public:
int a;
private:
int c;
};
struct D1 {
static int i; };
struct D2 {
int i; };
struct E1 {
static int i; };
struct E2 {
int i; };
struct D : public D1, public E1 {
int a; };
struct E : public D1, public E2 {
int a; };
struct F : public D2, public E2 {
static int a; };
struct G : public A
{
int foo;
A a;
};
struct H : public A
{
A a;
int foo;
};
int main()
{
cout << std::boolalpha;
cout << "is_standard_layout:" << std::endl;
//没有虚基类和虚函数,属于 standard_layout 类型
cout << "A: " << is_standard_layout<A>::value << endl;
//没有虚基类和虚函数,属于 standard_layout 类型
cout << "B: " << is_standard_layout<B>::value << endl;
//所有非静态成员访问权限不一致,不属于 standard_layout 类型
cout << "C: " << is_standard_layout<C>::value << endl;
//基类和子类没有同时出现非静态成员变量,属于 standard_layout 类型
cout << "D: " << is_standard_layout<D>::value << endl;
//没有虚基类和虚函数,属于 standard_layout 类型
cout << "D1: " << is_standard_layout<D1>::value << endl;
//基类和子类中同时出现了非静态成员变量,不属于 standard_layout 类型
cout << "E: " << is_standard_layout<E>::value << endl;
//多重继承中在基类里同时出现了非静态成员变量,不属于 standard_layout 类型
cout << "F: " << is_standard_layout<F>::value << endl;
//使用的编译器不同,得到的结果也不同。
cout << "G: " << is_standard_layout<G>::value << endl;
//子类中第一个非静态成员的类型与其基类类型不能相同,不属于 standard_layout 类型
cout << "H: " << is_standard_layout<H>::value << endl;
return 0;
}
- 测试:vs测试

- 测试:使用g++

6.总结
事实上,我们使用的很多内置类型默认都是 POD 的。POD 最为复杂的地方还是在类或者结构体的判断。
那么,使用 POD 有什么好处呢?
字节赋值,代码中我们可以安全地使用 memset 和 memcpy 对 POD 类型进行初始化和拷贝等操作。
提供对 C 内存布局兼容。C++ 程序可以与 C 函数进行相互操作,因为 POD 类型的数据在 C 与 C++ 间的操作总是安全的。
保证了静态初始化的安全有效。静态初始化在很多时候能够提高程序的性能,而 POD 类型的对象初始化往往更加简单。
参考:POD 类型
边栏推荐
- On the routing tree of gin
- 二分查找(整数二分)
- [Huawei cloud to Jian Zhiyuan] the winning list of essay solicitation is released!
- Bridging the gap between open source databases and database services
- Jenkins 通过检查代码提交自动触发编译
- 数字人民币可以买理财产品了!建行APP在试点地区上线服务专区,实测体验如何?
- 推進兼容適配,使能協同發展 GBase 5月適配速遞
- Promouvoir l'adaptation compatible et permettre le développement collaboratif du Service Express adaptatif gbase en mai
- Sdvo:ldso+ semantics, direct French Slam (RAL 2022)
- Batch export excel zip using zipfile, openpyxl and flask
猜你喜欢

Fast and accurate point cloud registration based on minimizing 3D NDT distance

得物App数据模拟平台的探索和实践

再次认识 WebAssembly

Wechat applet avatar pendant production

Ml notes matrix fundamental, gradient descent

C language learning -18-makefile file writing examples and how to generate and call dynamic libraries

Jenkins 通过检查代码提交自动触发编译

【newman】postman生成漂亮的测试报告
米哈游六月社招火热开启!500+岗位,超多HC,就在这个夏天(附内推方式)

Discover the number of media new users can insert
随机推荐
Process address space
Advanced thinking on application scenarios of standardization, maximum normalization and mean normalization
向量1(类和对象)
新版负载均衡WebClient CRUD
蓝桥杯2019年国赛最长子序列
【山大会议】项目初始化
Jenkins automatically triggers compilation by checking code submissions
向量2(友元及拷贝构造)
阿里云中间件的开源往事
CVE-2022-0847(提权内核漏洞)
英国考虑基于国家安全因素让Arm在伦敦上市
Uni develops wechat applet to customize automatic camera detection (portrait + ID card)
Simulation of vector
Huawei cloud hcdez special session and Distributed Technology Summit: Huawei cloud distributed cloud native technology and Practice
High precision calculation
Pymssql Module User Guide
C# 实现插入排序
【单片机】【让蜂鸣器发声】认识蜂鸣器,让蜂鸣器发出你想要的声音
快速排序quick_sort
微信小程序头像挂件制作