首页 > 其他分享 >POD类型

POD类型

时间:2023-12-08 15:23:20浏览次数:28  
标签:struct int value 类型 POD public 构造函数

文章参考:

爱编程的大丙 (subingwen.cn)

1. POD概述

1.1 意义

POD:是plain old data的缩写,即普通的旧数据。POD通常用于说明一个类型的属性,尤其是用户自定义类型的属性,具体来说它是指没有使用面向思想来设计的类/结构体。

POD含义为:

  • Plain:表示是一个普通的类型。(说明支持静态初始化)
  • Old:体现了与C地兼容性,支持标准C函数。(说明与C语言有一样的内存布局)

在C++11中将POD划分为两部分:

  • 平凡的trivial
  • 标准布局的standard layout

1.2 作用

  • 字节赋值:对于POD类型,我们可以安全的使用memsetmemcpy进行初始化和拷贝等操作。
  • 提供对C内存布局兼容:POD类型的数据在C和C++之间的操作总是安全的,因此C++程序可以和C函数进行相互操作。
  • 保证了静态初始化的安全有效:静态初始化在很多时候可以提高程序的性能,而POD类型的对象初始化往往更加简单。

2. "平凡"类型

一个平凡的类/结构体要同时满足以下几点要求:

  1. 拥有平凡的默认构造函数(trivial constructor)和平凡的默认析构函数(trivial destructor)。

    • 一般而言,如果不定义构造函数,那么编译器会为我们生成一个平凡的默认构造函数

      class Test{}		// 编译器生成平凡的默认构造函数
      
    • 一旦定义了构造函数,即使没有参数,函数体没有代码,那么该构造函数也不是“平凡”的。

      class Test{
          Test(){}		// 不是平凡的默认构造函数
      }
      
    • 例外在于:可以使用= default关键字,来显式地声明默认地构造函数,从而使类型恢复平凡化

      class Test{
      	Test() = default {}			// 显式声明平凡的构造函数
      }
      
    • 析构函数同理。

  2. 拥有平凡的拷贝构造函数(tivial copy constructor)和平凡的移动构造函数(trivial move constructor)

    • 平凡的拷贝构造函数基本上等同于使用memcpy进行类型的构造。
    • 同平凡的默认构造函数一样,在不声明拷贝构造函数地时候,编译器会自动帮程序员生成。
    • 如果定义了拷贝构造函数,那么可以使用=default来显式声明拷贝构造函数。
    • 移动构造函数和拷贝构造函数同理。
  3. 用于平凡的拷贝赋值运算符(trivial assignment operator)和平凡的移动赋值运算符(trivial assignment operator)。

    • 逻辑与第2条一致。
  4. 不包含虚函数和虚基类:

    • 虚函数:使用virtual修饰的函数叫做虚函数。
    • 虚基类:继承时使用virtual关键字修饰基类。

3. “标准布局”类型

标准布局主要指的是结构体地结构或者组合方式。

要成为标准布局,需要同时满足下列五点定义(前两条最重要):

  1. 所有非静态成员都有相同的访问权限。

    class Test{
    private:
        static int a;
    public:
        int b;
        int c;
        Test(){}
        void func_1();
    }
    
  2. 在类或者结构体继承时,继承树中只能有一个类有非静态成员变量。

    struct Base{static int a;};
    struct Child: public Base{int b;};		// ok
    struct Base1{int a;};
    struct Child1: public Base1{static int b;};		// ok
    struct Child2: public Base, public Base1{static int b;}		// ok
    struct Child3: public Base1{int b;};		// error
    struct Child4: public Base1, public Child{static int num;};		// error
    
  3. 子类中第一个非静态成员的类型不可以是基类类型。

    struct Base{};
    struct Child1: public Base{
        Base b;			// 这不是一个标准布局,因为第一个非静态成员是基类的类型
        int a;
    }
    struct Child2: public Base{
        static Base b1;
        int a;			// 是一个标准布局,子类的第一个非静态成员不是基类的类型
        Base b2;
    }
    

    注:这条规则主要是为了节约内存,提高数据的读取效率。对于上述两种继承情况,在基类没有成员的情况下,两个子类Child1Child2的内存结构是不一样的:

    • Child1:如果子类的第一个非静态成员仍是基类类型,C++标准要求类型相同的对象它们的地址必须不同,此时需要分配额外的地址空间将二者的地址错开。
    • Child2:如果子类的第一个非静态成员不是基类类型,C++标准允许标准布局类型派生类的第一个成员和基类这个类本身共享地址,此时基类变量实际上没有占据任何的实际空间
  4. 没有虚函数和虚基类。

  5. 所有非静态数据成员均符合标准布局类型,其基类也符合标准布局。(这是一个递归的定义)

4. 判断是否为POD类型

4.1 对“平凡“进行判断

C++11提供类模板std::is_trivial来检测一个类型是否trivial,定义如下:

template <class T>
struct std::is_trivial

通过模板类的成员value,可以获取类型T是否是平凡的,返回一个布尔值,语法如下:

std::is_trivial<T>::value;

EG:

  • 代码:

    #include <iostream>
    using namespace std;
    
    struct A{};
    struct B{
    public:
        B(){}
    };
    struct C: public A{};
    struct D: public B{};
    struct E: virtual public A{};
    struct F{
    public:
        virtual void func(){}
    };
    
    int main(void){
        cout << "A:" << is_trivial<A>::value << endl;
        cout << "B:" << is_trivial<B>::value << endl;
        cout << "C:" << is_trivial<C>::value << endl;
        cout << "D:" << is_trivial<D>::value << endl;
        cout << "E:" << is_trivial<E>::value << endl;
        cout << "F:" << is_trivial<F>::value << endl;
        cout << "int:" << is_trivial<int>::value << endl;
        using Arr = A[];
        cout << "A[]:" << is_trivial<Arr>::valur << endl;
        return 0;
    }
    
  • 输出:

    A:1
    B:0
    C:1
    D:0
    E:0
    F:0
    int:1
    A[]:1
    
  • 分析:

    • A:拥有默认的构造函数、析构函数,属于trivial类型。
    • B:自定义了构造函数,因此不属于trivial类型。
    • C:虽然继承了A,但A也不含不符合标准的内容,因此属于trivial类型。
    • D:基类自定义了构造函数,继承后D内也相当于有了构造函数,因此不属于trivial类型。
    • E:继承关系中有虚基类,因此不属于trivial类型。
    • F:类成员函数中有虚函数,因此不属于trivial类型。
    • int:标准内置数据类型,属于trivial
    • A[]:元素是平凡类型的数组总是平凡的,因此属于trivial类型。

4.2 对“标准布局”类型的判断

在C++11中,提供了模板类std::is_standard_layout来判断一个类型是否是为标准布局,定义如下:

template <typename T>
struct std::is_standard_layout;

通过模板类的成员value,可以获取类型T是否为标准布局,返回一个布尔值,语法如下:

std::is_standard_layout<T>::value

EG

  • 代码:

    #include <iostream>
    using namespace std;
    
    struct A{};
    struct B: public A{int a;};
    struct C{
    private:
        int a;
    public:
        int b;
    };
    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(void){
        cout << "A:" << is_standard_layout<A>::value << endl;
        cout << "B:" << is_standard_layout<B>::value << endl;
        cout << "C:" << is_standard_layout<C>::value << endl;
        cout << "D:" << is_standard_layout<D>::value << endl;
        cout << "E:" << is_standard_layout<E>::value << endl;
        cout << "F:" << is_standard_layout<F>::value << endl;
        cout << "G:" << is_standard_layout<G>::value << endl;
        cout << "H:" << is_standard_layout<H>::value << endl;
        return 0;
    }
    
  • 编译执行:

    g++ test.cpp -o test -std==c++11
    ./test
    
  • 输出:

    A:1
    B:1
    C:0
    D:1
    E:0
    F:0
    G:1
    H:0
    
  • 分析:

    • A:符合五条要求,是标准布局类型。
    • B:符合五条要求,是标准布局类型。
    • C:违反了第一条:所有非静态成员应该有相同的访问权限,因此不是标准布局类型。
    • D:符合五条要求,是标准布局类型。(虽然有继承,但继承树中只有一个类有非静态成员变量,因此不违反要求)
    • E:违反了第二条:在类或者结构体继承时,继承树中只能有一个类有非静态成员变量,因此不是标准布局类型。
    • F:违反了第二条:在类或者结构体继承时,继承树中只能有一个类有非静态成员变量,因此不是标准布局类型。
    • G:符合五条要求,是标准布局类型。
    • H:违反了第三条规则:子类中第一个非静态成员的类型不可以是基类类型,因此不是标准布局类型。

标签:struct,int,value,类型,POD,public,构造函数
From: https://www.cnblogs.com/beasts777/p/17888236.html

相关文章

  • How to connect two pairs of AirPods to one phone simultaneously
    TechStreamingHomeKitchenHealthStyleBeautyGiftsDealsMore REVIEWS  TECHHowtoconnecttwopairsofAirPodstoonephonesimultaneouslyWrittenby AbigailAbesamisDemarest and DevonDelfino; editedby ElenaMatarazzo Updated......
  • Java-引用类型
    Java-引用类型四种引用类型(强引用、软引用、弱引用、虚引用)在Java中具有不同的使用场景,可以根据程序的需求和内存管理的要求来选择适当的引用类型。1.强引用(StrongReference):使用场景:在绝大多数情况下,我们使用的都是强引用。当一个对象具有强引用时,垃圾回收器不会回收该对......
  • “数组”类型(python中都可[]的一些数据类型)
    “数组”类型(python中都可[]的一些数据类型)list特点是可变的、能动态扩容,可存储Python中的一切对象,使用时不用指定存储的元素的类型。>>>arr=["one","two","three"]>>>arr[0]'one'#动态扩容>>>arr.append(4)>>>arr['one',......
  • UML各类型含义
    箭头含义:导语在平时的开发中,难免会遇到画UML图的时候,也就是我们所说的类图,但是UML图中的箭头多种多样,所代表的含义也是各不相同,今天我们就来说说这几种箭头所代表的含义。1泛化概念:泛化表示一个更泛化的元素和一个更具体的元素之间的关系。泛化是用于对继承进行建模的UML元素......
  • Java基本数据类型转换
    1.Java基本数据类型转换1.1自动类型转换当Java程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型这个就是自动类型转换数量类型按精度大小排序为:char<int<long<float<doublebyte<short<int<long<float<doubleinta='c';doubled=80注意:有多种类型......
  • Java数据类型
    1.数据类型每一种数据都定义了明确的数据类型,在内存中分配大小不同的内存空间(字节)8大基本数据类型整数类型:存放整数byte,1个字节short,2个字节int,4个字节(默认)long,8个字节浮点类型(小数)float,4个字节double,8个字节(默认)字符型:char,2个字节布尔型:boolean,1个字......
  • 各种类型数据库的连接字符串
    DataType.MySqlDataSource=127.0.0.1;Port=3306;UserID=root;Password=root;DataBase=cccddd;Charset=utf8;SslMode=none;Minpoolsize=1DataType.PostgreSQLHost=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Minimu......
  • C 语言实现抽象数据类型(ADT)之链表
    C语言实现抽象数据类型(ADT)之链表1什么是链表?(懂跳)C语言本身自带了很多基本数据类型,每种基本数据类型的变量总是代表着某个数据,比如:我们通常用整型变量来计数,用浮点型变量来保存价格这样的数据……intcount;doubleprice;而有时候我们需要表示的数据很复杂,比如我们想要......
  • UE蓝图类型转换object引用
    一.常用的记住1.角色之间,获取玩家角色,玩家控制器2.动画蓝图,获取玩家动画3.在游戏模式中记录变量,方便类型转换。但是会影响运行速度4.playerstate优先与gamemode可以使用二.对象发生关系1.重叠对象,触发,打击,引出引脚actor2.getallactor寻找,(getwigdet控件也可以这样找),......
  • 一端为P2P网络类型,另一端为广播网络类型,为何无法学习到路由?
    配置:R1:[V200R003C00]#sysnamer1#snmp-agentlocal-engineid800007DB03000000000000snmp-agent #clocktimezoneChina-Standard-Timeminus08:00:00#portallocal-serverloadportalpage.zip#dropillegal-macalarm#setcpu-usagethreshold80resto......