c++ trivial, standard layout和POD类型解析
1. trivial类型
占用一片连续的内存,编译器可以重排成员变量的顺序或者增加一些padding(为了对齐),因此,可以放心的使用memcpy等函数,
但是,在c代码里面使用可能会出问题(因为可能会被重排),有如下特点:
- 没有虚函数和虚基类
- 基类也必须保证没有non-trivial的构造/析构/operator函数
- 成员变量的类型也必须保证没有non-trivial的构造/析构/operator函数
- 成员变量可以有public/protected/private修饰,且可以出现多个。
- 构造/operator/析构可以有自定义的,但是必须有默认的
可以看出来,trivial主要是针对于 构造/operator/析构 三种函数限制的,普通函数只要不是虚函数,就没有任何限制。
题外话,关于重排序,来看一个例子:
struct Foo {
A a;
B b;
C c;
private:
D d;
E e;
F f;
};
一般来讲,abc的顺序是不会重排的,def的顺序是不会重排的,但是,因为abc和def拥有着不同的访问控制符,是有可能def重排到abc前面的。官方解释
如下:
Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated
so that later members have higher addresses within a class object.
The order of allocation of nonstatic data members separated by an access-specifier is unspecified (11.1)
注意,静态成员变量不影响
好了,定义部分讲完了,来看几个例子:
//trivial类型,同时也是pod类型(后面会解释)
class TrivialClass{
int a_;
bool flag_;
//bool flag2_{false}; //这种就不是trivial了
};
//不是trivial类型
class NonTrivialClass{
int a_;
bool flag_;
bool flag2_{false}; //比上面就多了一个初始化赋值,就不是trivial了,但是这个是stdlayout(后续会介绍)
};
class NonTrivialClass2{
int a_;
bool flag_;
bool flag2_ = false; //同NonTrivialClass
};
//不是trivial类型
class NonTrivialClass3{
NonTrivialClass3() {} //和NonTrivialClass3() = default的写法是不一样的,决定是否为trivial类型
int a_;
bool flag_;
bool flag2_ = false; //同NonTrivialClass
};
//下面的也满足trivial要求
class TrivialClass2{
public:
//注意,静态类型是不影响判断的,因为静态成员变量不会存储在结构体/类里面
static std::string static_member;
//也可以有构造函数,但是必须有默认的构造函数才行
TrivialClass2(int a, int b): a_(a), b_(b) {}
TrivialClass2() = default;
void foo() {}
int a_;
private:
int b_;
bool flag_;
};
虽然举的例子都是没有继承的简单类,但是如果有继承,也有可能是trivial类型,需要保证基类也是trivial类型
2. standard layout类型
standard layout类型(以下简称stdlayout):不包含任何c语言里没有的功能,可以使用memcpy等函数,并且可以在c代码里放心使用,这点不同于trvial的类型,同时,stdlayout类型也能拥有自己的函数,有如下特点:
- 没有虚函数和虚基类
- 所有的非静态成员变量必须拥有完全一样的public/protected/private访问控制符, 比如下面的
TrivialClass2就不是stdlayout类型 - 所有非静态成员变量也必须是stdlayout类型
- 所有的基类也必须是stdlayout
- 不能把基类类型作为第一个非静态成员变量
- 下面的条件满足一个即可(总结就是要么子类有非静态成员变量,要么父类有):
- 子类没有非静态成员变量并且只有一个基类有非静态成员变量
- 基类都没有非静态成员变量
注意,静态成员变量不影响
可以看出来,stdlayout放宽了构造/operator/析构的限制,但是也收紧了访问控制符的限制(只能有一种)
接下来看代码示例:
//满足stdlayout的要求,但是因为没有默认的构造函数,因此不满足trivial的要求
class StdLayoutClass{
public:
StdLayoutClass(int a, int b): a_(a), b_(b) {}
int a_;
int b_;
};
//和上面的结构体就多了一个private(权限修饰符不一样),这里就不是stdlayout了
class ClassMixedAccess{
public:
ClassMixedAccess(int a, int b): a_(a), b_(b) {}
int a_;
private:
int b_;
};
class StdLayoutBase{
public:
StdLayoutBase() {} //和StdLayoutBase()=default的写法是有区别的,决定了是否为trivial
int a_;
int b_;
};
//这个不是standard layout,因为基类有非静态成员变量,也不是trivial,因为积累的构造函数不是default(注意,和=default的写法是有区别的)
class StdLayoutDerived : public StdLayoutBase {
int c_;
int d_;
};
class StdLayoutBase2{
StdLayoutBase2() = default;
};
//这个就满足stdlayout的要求了,因为必须满足子类或者基类只能有一个有非静态成员变量
class StdLayoutDerived2 : public StdLayoutBase2 {
StdLayoutDerived2() = default;
int c_;
int d_;
//可以拥有自己的函数
void test_func(){
printf("hello world");
}
};
3. 集大成者,POD(Plain Old Data)类型
POD类型:当一个类既是trivial又是stdlayout类型的时候,这个时候它就是pod类型,因此,pod类型的特点如下:
- 拥有着连续的内存
- 每一个成员变量的地址都高于其前一个成员变量(也就是没有进行重排序操作)
- 同样的,也有嵌套要求,非静态成员变量必须也是POD类型
综上,POD类型可以在I/O操作中放心的进行copy和恢复, 基本类型比如int,char,引用等都是POD类型。
注意,静态成员变量不影响。
示例代码如下:
class ClassWithVirtual{
public:
virtual bool foo() {}
};
//出现了虚函数,因此,trivial和stdlayout都不是
class ClassWithVirtualDerived : public ClassWithVirtual {
public:
int a_;
int b_;
virtual bool foo() override{}
};
//有不同的访问限定符,因此,属于trivial但是不属于stdlayout
class ClassDiffAccess{
public:
int a_;
private:
int b_;
};
//拥有着自定义的构造函数(但是没有显示指定default),因此不属于trivial
class ClassWithoutDefaultConstructor{
public:
ClassWithoutDefaultConstructor() {};
int a_;
int b_;
};
//集大成者,既属于trivial也属于stdlayout,也就是POD类型
class PODClass {
int a_;
int b_;
};
4. 测试代码
上述类,可以通过如下代码测试
#define BOOL_2_STRING(val) ((val) ? "true" : "false")
/**
* tool function
* @tparam T
*/
template<class T>
void test_trivial_stdlayout_pod_type_internal(const char *type_name) {
bool is_trivial = std::is_trivial<T>::value;
bool is_std_layout = std::is_standard_layout<T>::value;
bool is_pod = std::is_pod<T>::value;
printf("%s: is_trivial[%s] is_standard_layout[%s] is_pod[%s]\n",
type_name, BOOL_2_STRING(is_trivial),
BOOL_2_STRING(is_std_layout), BOOL_2_STRING(is_pod));
}
/**
* 测试三大类型,trivial,standard layout, pod
*/
void test_trivial_stdlayout_pod_type() {
test_trivial_stdlayout_pod_type_internal<TrivialClass>("TrivialClass");
test_trivial_stdlayout_pod_type_internal<NonTrivialClass>("NonTrivialClass");
test_trivial_stdlayout_pod_type_internal<NonTrivialClass2>("NonTrivialClass2");
test_trivial_stdlayout_pod_type_internal<NonTrivialClass3>("NonTrivialClass3");
test_trivial_stdlayout_pod_type_internal<TrivialClass2>("TrivialClass2");
test_trivial_stdlayout_pod_type_internal<StdLayoutClass>("StdLayoutClass");
test_trivial_stdlayout_pod_type_internal<ClassMixedAccess>("ClassMixedAccess");
test_trivial_stdlayout_pod_type_internal<StdLayoutDerived>("StdLayoutDerived");
test_trivial_stdlayout_pod_type_internal<StdLayoutDerived2>("StdLayoutDerived2");
test_trivial_stdlayout_pod_type_internal<ClassWithVirtualDerived>("ClassWithVirtualDerived");
test_trivial_stdlayout_pod_type_internal<ClassDiffAccess>("ClassDiffAccess");
test_trivial_stdlayout_pod_type_internal<ClassWithoutDefaultConstructor>("ClassWithoutDefaultConstructor");
test_trivial_stdlayout_pod_type_internal<PODClass>("PODClass");
}
/*
输出如下:
TrivialClass: is_trivial[true] is_standard_layout[true] is_pod[true]
NonTrivialClass: is_trivial[false] is_standard_layout[true] is_pod[false]
NonTrivialClass2: is_trivial[false] is_standard_layout[true] is_pod[false]
NonTrivialClass3: is_trivial[false] is_standard_layout[true] is_pod[false]
TrivialClass2: is_trivial[true] is_standard_layout[false] is_pod[false]
StdLayoutClass: is_trivial[false] is_standard_layout[true] is_pod[false]
ClassMixedAccess: is_trivial[false] is_standard_layout[false] is_pod[false]
StdLayoutDerived: is_trivial[false] is_standard_layout[false] is_pod[false]
StdLayoutDerived2: is_trivial[true] is_standard_layout[true] is_pod[true]
ClassWithVirtualDerived: is_trivial[false] is_standard_layout[false] is_pod[false]
ClassDiffAccess: is_trivial[true] is_standard_layout[false] is_pod[false]
ClassWithoutDefaultConstructor: is_trivial[false] is_standard_layout[true] is_pod[false]
PODClass: is_trivial[true] is_standard_layout[true] is_pod[true]
*/
c++ trivial, standard layout和POD类型解析的更多相关文章
- C++中的Trivial 、POD、non-POD和Standard Layout概念
POD types non-POD types Standard Layout types A Formal Definition Informally, a standard layout clas ...
- C++ trivial和non-trivial构造函数及POD类型(转)
原博客地址http://blog.csdn.net/a627088424/article/details/48595525 最近正纠结这个问题就转过来了,做了点补充(参考<深度探索C++对象模型 ...
- 关于C++ 中POD类型的解析
转自: http://liuqifly.spaces.live.com/blog/cns!216ae3a149106df9!221.entry (C++-98:1.8;5)给出的定义:将对象的各字节拷 ...
- C++ POD 类型
POD 是 C++ 中一个比较重要的概念,POD 是英文 Plain Old Data 的缩写(通俗讲就是类或结构体通过二进制拷贝后还能保持其数据不变),用来描述一个类型(包括 class.union ...
- C++11 POD类型
POD,全称plain old data,plain代表它是一个普通类型,old代表它可以与c兼容,可以使用比如memcpy()这类c中最原始函数进行操作.C++11中把POD分为了两个基本概念的集合 ...
- 3. C++ POD类型
POD全称Plain Old Data,通常用于说明1个类型的属性.通俗的讲,一个类或结构体通过二进制拷贝后还能保持其数据不变,那么它就是一个POD类型. C++11将POD划分为2个基本概念的合集, ...
- 聚合类型与POD类型
Lippman在<深度探索C++对象模型>的前言中写道: I have heard a number of people over the years voice opinions sim ...
- c++11 pod类型(了解)
啥是POD类型? POD全称Plain Old Data.通俗的讲,一个类或结构体通过二进制拷贝后还能保持其数据不变,那么它就是一个POD类型. 平凡的定义 .有平凡的构造函数 .有平凡的拷贝构造函数 ...
- SpringMVC中对多部件类型解析---文件(图片)上传
加入上传图片jar包 commons-io-2.4.jar commons-fileupload-1.3.jar 在页面form中提交enctype="multipart/form-data ...
- 关于POD和非POD类型中,list initialization和constructor initialization(未解决)
如果你的成员是POD类型的,那么list initialization和constructor initialization没有任何区别 #include<iostream> using ...
随机推荐
- 输入法词库解析(一)百度自定义方案.def
详细代码:https://github.com/cxcn/dtool 前言 .def 是百度手机输入法-更多设置-自定义输入方案所使用的格式. 解析 码表偏移量 0x6D # 占用字节数 描述 a 1 ...
- 若依(RuoYi )权限管理设计
前言 若依权限管理包含两个部分:菜单权限 和 数据权限.菜单权限控制着我们可以执行哪些操作.数据权限控制着我们可以看到哪些数据. 菜单是一个概括性名称,可以细分为目录.菜单和按钮,以若依自身为例: 目 ...
- (一)JPA的快速入门
JPA简介 JPA是什么 JPA 是Java Persistence API的缩写,是一套由Java官方制定的ORM标准.当制定这套标准以后,市场上就出现很多JPA框架.如:OpenJPA(apach ...
- vue中修改滚动条样式
这是一个在VUE中的设置滚动条样式的方法,该方法只适用于Chrome浏览器,亲测在火狐不适用 样式写在 APP.vue 中,可以用在全局,如果只用在那个盒子中,只需要在::前面加上盒子的class名就 ...
- value中放vue的参数
<input type="text" v-model="userInfo.name"/>
- 洛谷P1638 逛画展 (尺取法)
尺取法的经典题目: 博览馆正在展出由世上最佳的 mm 位画家所画的图画. 游客在购买门票时必须说明两个数字,aa 和 bb,代表他要看展览中的第 aa 幅至第 bb 幅画(包含 a,ba,b)之间的所 ...
- spring boot使用swagger生成api接口文档
前言 在之前的文章中,使用mybatis-plus生成了对应的包,在此基础上,我们针对项目的api接口,添加swagger配置和注解,生成swagger接口文档 具体可以查看本站spring boot ...
- MySQL之安装(linux两种版本版本安装)
LinuxMySQL安装(Mysql5.5版本) 第一种 有安装包的安装方式 1.下载地址: http://dev.mysql.com/downloads/mysql 2.检查当前系统是否安装过mys ...
- 2022年最新最详细IDEA关联数据库方式、在IDEA中进行数据库的可视化操作(包含图解过程)
文章目录 1.使用IDEA关联Mysql数据库的详细操作步骤 1.1 打开侧边栏的Database 2.2. 选择要连接的数据库(Mysql) 2.3 .输入要连接的数据库.用户名.密码 2.4 .点 ...
- python-windows下如何将单个py文件生成exe文件
这需要pyinstaller,在cmd执行pip install pyinstaller命令 安装完成后,执行 pyinstaller -F 目录 可执行文件于是就在同个目录下的dist目录顺利生成了 ...