c++ trivial, standard layout和POD类型解析

   2023-02-09 学习力0
核心提示:目录1. trivial类型2. standard layout类型3. 集大成者,POD(Plain Old Data)类型4. 测试代码1. trivial类型占用一片连续的内存,编译器可以重排成员变量的顺序或者增加一些padding(为了对齐),因此,可以放心的使用memcpy等函数,但是,在c代码里面使用

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]
*/
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • VScode运行C++中文终端乱码的解决方案 vscode错误提示中文
    VScode运行C++中文终端乱码的解决方案 vscode错
    目录问题原因查看VSCODE编码方式查看终端编码方式解决办法更改VSCODE编码方式选通过编码保存选择编码方式为gbk总结问题Vscode编辑器中中文显示正常F5调试运行后中文显示乱码原因原因是VSCODE编辑器的编码和终端的编码不一致。VSCODE为utf-8,而cmd的默认编码
    03-08
  • C++ LeetCode1945题解字符串转化后的各位数字之和
    C++ LeetCode1945题解字符串转化后的各位数字
    目录1945.字符串转化后的各位数字之和方法一:计算AC代码C++1945.字符串转化后的各位数字之和力扣题目链接:leetcode.cn/problems/su…给你一个由小写字母组成的字符串 s ,以及一个整数 k 。首先,用字母在字母表中的位置替换该字母,将 s 转化 为一个整数(
  • C++ sdl实现渲染旋转视频的方法分享
    C++ sdl实现渲染旋转视频的方法分享
    目录前言一、如何实现1、计算边框大小2、计算缩放大小3、逆运算视频宽高二、完整代码三、使用示例总结前言一般情况下播放视频时不需要旋转,但是如果是移动端录制的视频有时会出现rotate参数,且视频宽高也是互换的,如果直接渲染则会出现视频90度倒转的问题
  • C++ LeetCode0547题解省份数量图的连通分量
    C++ LeetCode0547题解省份数量图的连通分量
    目录LeetCode 547.省份数量方法一:BFS求图的连通分量AC代码C++LeetCode 547.省份数量力扣题目链接:leetcode.cn/problems/nu…有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城
  • C++ LeetCode1832题解判断句子是否为全字母句
    目录LeetCode 1832.判断句子是否为全字母句方法一:统计AC代码C++LeetCode 1832.判断句子是否为全字母句力扣题目链接:leetcode.cn/problems/ch…全字母句 指包含英语字母表中每个字母至少一次的句子。给你一个仅由小写英文字母组成的字符串 sentence ,请
  • C++ LeetCode1781题解所有子字符串美丽值之和
    C++ LeetCode1781题解所有子字符串美丽值之和
    目录LeetCode 1781.所有子字符串美丽值之和方法一:前缀和AC代码C++方法二:边遍历边计算AC代码C++LeetCode 1781.所有子字符串美丽值之和力扣题目链接:leetcode.cn/problems/su…一个字符串的 美丽值 定义为:出现频率最高字符与出现频率最低字符的出现次
  • C++ LeetCode300最长递增子序列
    C++ LeetCode300最长递增子序列
    目录LeetCode 300.最长递增子序列方法一:动态规划AC代码C++LeetCode 300.最长递增子序列力扣题目链接:leetcode.cn/problems/lo…给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。子序列 是由数组派生而来的序列,删除(或不删除)数组中的元
  • C++ LeetCode1780判断数字是否可以表示成三的幂的和
    C++ LeetCode1780判断数字是否可以表示成三的
    目录LeetCode 1780.判断一个数字是否可以表示成三的幂的和方法一:二进制枚举题目分析解题思路复杂度分析AC代码C++方法二:进制转换AC代码C++LeetCode 1780.判断一个数字是否可以表示成三的幂的和力扣题目链接:leetcode.cn/problems/ch…给你一个整数 n 
  • C++使用宏实现动态库加载 c++加载静态库
    目录前言一、为什么使用宏1、Windows加载2、Linux加载3、宏加载二、具体实现三、如何使用1、引用头文件2、添加导入宏3、直接调用总结前言开发的时候,有些项目不能静态链接动态库,需要程序运行时加载动态库,这个时候根据不同平台我们通常使用LoadLibrary或d
  • C++ LeetCode1805字符串不同整数数目
    目录LeetCode 1805.字符串中不同整数的数目方法一:遍历拆分AC代码C++LeetCode 1805.字符串中不同整数的数目力扣题目链接:leetcode.cn/problems/nu…给你一个字符串 word ,该字符串由数字和小写英文字母组成。请你用空格替换每个不是数字的字符。例如,"a12
点击排行