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]
*/

最新文章

  1. &lt;六&gt;JDBC_DAO 设计模式
  2. C++是一把很奇怪的刀
  3. 11.21 if条件语句 年月日执行判断
  4. Web Api 中Get 和 Post 请求的多种情况分析
  5. ios 项目里常用的宏
  6. NYOJ 52-无聊的小明
  7. POJ 2001
  8. 查看Eclipse32位还是64位以及Eclipse的编译版本号,查看JDK是32位还是64位
  9. 《一步一步写嵌入式操作系统》读书笔记1—Skyeye介绍、安装和HelloWorld
  10. zTree判断是否为父节点
  11. 记WebUtility.HtmlDecode将&amp;nbsp;转成特殊空格的问题
  12. Windows Phone开发(29):隔离存储C
  13. 利用Scrapy爬取所有知乎用户详细信息并存至MongoDB
  14. HTTPS从认识到线上实战全记录
  15. Haproxy原理(1)
  16. Java Spring Boot VS .NetCore (六) UI thymeleaf vs cshtml
  17. Lazarus下面的javascript绑定另外一个版本bug修正
  18. bzoj1212(trie+dp)
  19. jQuery获取动态产生的html内标签或元素
  20. git reset之后找回本地未提交的代码

热门文章

  1. Linux安装Minio
  2. PHP函数小工具
  3. nginx日志参数及含义
  4. 第四章:Django表单 - 4:表单的Widgets
  5. Docker 部署 Confluence(破解版)
  6. Elasticsearch:Split index API - 把一个大的索引分拆成更多分片
  7. Oracle基础知识汇总一
  8. C++面向对象编程之成员模板、模板特化、偏特化和模板模板参数
  9. 使用开源计算引擎提升Excel格式文件处理效率
  10. Android自动化测试工具调研