• C++11 POD类型


    POD,全称plain old data,plain代表它是一个普通类型,old代表它可以与c兼容,可以使用比如memcpy()这类c中最原始函数进行操作。C++11中把POD分为了两个基本概念的集合,即:平凡的(trival)和标准布局的(standard layout)。

    1、首先是平凡的(trival)定义,通常一个平凡的类或者结构体需要满足以下定义

      (1).拥有平凡的默认构造函数和析构函数。默认的意思就是由编译器为我们自动生成的,不许是我们自己定义的,而一旦定义了构造函数,即使函数体里没有任何代码,那么该构造函数也不在是平凡的,但是由于c++11提供了default,也可以是自己定义的加=default。

      (2).拥有平凡的拷贝构造函数和移动构造函数。默认的意思同上,也可以使用=default。

      (3).拥有平凡的拷贝赋值运算符和移动赋值运算符。

      (4).不能包含虚函数和虚基类。

    C++11中,我们使用模版类std::is_trivial<T>::value来判断数据类型是否为平凡类型。

    #include "stdafx.h"
    #include <iostream>
    
    using namespace std;
    
    class A { A(){} };
    class B { B(B&){} };
    class C { C(C&&){} };
    class D { D operator=(D&){} };
    class E { E operator=(E&&){} };
    class F { ~F(){} };
    class G { virtual void foo() = 0; };
    class H : G {};
    class I {};
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::cout << std::is_trivial<A>::value << std::endl;  //有不平凡的构造函数
        std::cout << std::is_trivial<B>::value << std::endl;  //有不平凡的拷贝构造函数
        std::cout << std::is_trivial<C>::value << std::endl;  //有不平凡的拷贝赋值运算符
        std::cout << std::is_trivial<D>::value << std::endl;  //有不平凡的拷贝赋值运算符
        std::cout << std::is_trivial<E>::value << std::endl;  //有不平凡的移动赋值运算符
        std::cout << std::is_trivial<F>::value << std::endl;  //有不平凡的析构函数
        std::cout << std::is_trivial<G>::value << std::endl;  //有虚函数
        std::cout << std::is_trivial<H>::value << std::endl;  //有虚基类
        std::cout << std::is_trivial<I>::value << std::endl;  //平凡的类return 0;
    }
    2.接下来是标准布局的定义

    (1).所有非静态成员拥有相同的访问级别,(访问级别就是public,private,protected)

    struct t1{
    private :
      int a;
    public:
      int b;
    };       //不满足标准布局,因为a,b访问级别不同。

    (2).在类和结构体继承时需要满足以下两个情况之一:

      派生类中有非静态类,那么这个派生类只能有且只有一个仅包含了静态成员的基类。

      基类有非静态成员,那么派生类中不允许有非静态成员。

    (这两句话看着挺绕口,其实就是在说明一个事实,关于非静态数据的事实,派生类中有非静态的数据那么它的基类只能是只有静态的,而且基类只能有一个。如果基类有非静态的, 那么派生类就不能有非静态的。有种跷跷板的感觉,非静态的对面坐着的是静态,父子类就 是坐在跷跷板的两端这种对应关系。)

    (3).类中第一个非静态类型与基类不是同一个类型。比如

    struct A:B{
        B b;
        int c;
    }    //不符合这个条件。因为A中第一个成员是基类B类型的

    (4).没有虚类和虚基类(与trival中重复)

    (5).所有非静态数据成员都符合标准布局的要求,这其实就是一个递归的定义。

    C++11中,我们使用模版类std::is_standard_layout<A>::value来判断类型是否是一个标准布局类型。

    所以在C++11中,POD就是满足平凡的(trival)和标准布局(standard layout)这两个方面。
    可以使用<type_traits>中的is_pod<T>::value判断T是不是POD类型的。
    #include <iostream>
    
    using namespace std;
    
    class A {
    private:
        int a;
    public:
        int b;
    };
    
    class B1 {
        static int x1;
    };
    
    class B2 {
        int x2;
    };
    
    class B : B1, B2 {
        int x;
    };
    
    class C1 {};
    class C : C1 {
        C1 c;
    };
    
    class D { virtual void foo() = 0; };
    class E : D {};
    class F { A x; };
    
    int main()
    {
        std::cout << std::is_standard_layout<A>::value << std::endl;  //违反定义1,成员a和b具有不同的访问权限
        std::cout << std::is_standard_layout<B>::value << std::endl;  //违反定义2,继承树有两个(含)以上的类有非静态成员
        std::cout << std::is_standard_layout<C>::value << std::endl;  //违反定义3,第一个非静态成员是基类类型
        std::cout << std::is_standard_layout<D>::value << std::endl;  //违反定义4,有虚函数
        std::cout << std::is_standard_layout<E>::value << std::endl;  //违反定义5,有虚基类
        std::cout << std::is_standard_layout<F>::value << std::endl;  //违反定义6,非静态成员x不符合标准布局类型
        return 0;
    }
    View Code
    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    class AA
    {
    public:
        int x;
        double y;
    };
    
    int main()
    {
        if (std::is_pod<AA>::value) {
            cout << "before" << endl;
            AA aa;
            aa.x = 10;
            aa.y = 20.0f;
            cout << aa.x << " " << aa.y << endl;
    
            size_t size = sizeof(aa);
            char *p = new char[size];
            memcpy(p, &aa, size);
    
            AA *pA = (AA*)p;
            cout << "after" << endl;
            cout << pA->x << " " << pA->y << endl;
            delete p;
        }
        return 0;
    }
    View Code

    说了这么多,那么为什么我们需要POD这种条件满足的数据呢?

      (1).可以使用字节赋值,比如memset,memcpy操作

      (2).对C内存布局兼容。

      (3).保证了静态初始化的安全有效。

  • 相关阅读:
    vpp + vxlan
    vpp + frrouting
    VPP + vxlan
    dpdk: Unsupported PCI device 0x19e5:0x0200 found at PCI address 0000:05:00.0
    How-to: Build VPP FD.IO with Mellanox DPDK PMD on top CentOS 7.7 with inbox drivers.
    vpp[73384]: register_node:485: process stack: Invalid argument (errno 22)
    鲲鹏920上vpp--dpdk编译
    编译frr--python版本问题--Python-3.7.7
    安装vpp
    Go排序
  • 原文地址:https://www.cnblogs.com/DswCnblog/p/6371071.html
Copyright © 2020-2023  润新知