C++中的POD型别

  最近在阅读《STL源码刨析》的过程中,遇到了POD型别,书中的解释是: 

  POD意指Plain Old Data,也就是标量型别(scalar types)或传统的C struct型别。POD型别必然拥有trivial ctor/dctor/copy/assignment operator函数。(这里的trivial 函数意思是该函数无光痛痒,不重要,即平凡函数)

  但是看完这个解释,我还是没有很清楚的理解所谓POD型别,通过在网上查阅资料,我对POD有了更好的理解。网上讲到一种判断POD型别的方式,即当一个数据类型同时满足”平凡的定义“和”标准布局“,我们可以认为其是一个POD型别的数据。

 

平凡的定义

平凡的结构体

  对于一个类或结构体来讲,平凡的定义是:

  • 平凡的构造/拷贝构造/移动构造函数
  • 平凡的拷贝复制/移动复制运算符
  • 平凡的析构函数
  • 不包含虚函数、虚基类

  测试代码:

#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 main()
{
    std::cout << "有不平凡的构造函数" << ends << std::is_trivial<A>::value << std::endl;        
    std::cout << "有不平凡的拷贝构造函数" << ends << std::is_trivial<B>::value << std::endl;
    std::cout << "有不平凡的移动构造函数" << ends << std::is_trivial<C>::value << std::endl;
    std::cout << "有不平凡的拷贝赋值运算符" << ends << std::is_trivial<D>::value << std::endl;
    std::cout << "有不平凡的移动赋值运算符" << ends << std::is_trivial<E>::value << std::endl;
    std::cout << "有不平凡的析构函数" << ends << std::is_trivial<F>::value << std::endl;        
    std::cout << "有虚函数" << ends << std::is_trivial<G>::value << std::endl;        
    std::cout << "有虚基类" << ends << std::is_trivial<H>::value << std::endl;            
    std::cout << "平凡的类" << ends << std::is_trivial<I>::value << std::endl;            

    system("pause");
    return 0;

}

运行结果:

测试环境:VS2019

 

平凡的函数

  这里所提到的平凡的构造、析构函数,就是指编译器自动生成的构造、析构函数等,也就是说一个POD型别的数据是不能有用户自定义的构造/拷贝构造/移动构造函数,拷贝复制/移动复制运算符以及析构函数。

标准布局的定义

  • 所有非静态成员有相同的访问权限
  • 继承树中最多只有一个类有非静态数据成员
  • 子类的第一个非静态成员不可以是基类类型
  • 没有虚函数、虚基类
  • 所有非静态成员都符合标准布局类型

 

测试代码:

#include <iostream>
using namespace std;

//成员a和b具有不同的访问权限
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 {};
//非静态成员x不符合标准布局类型
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不符合标准布局类型

    system("pause");
    return 0;
}

运行结果:

测试环境:VS2019

 

POD型别的作用

  为什么C++需要引进POD这一个概念?我结合自己的理解和网上的资料总结出以下几点:

  • POD型别是兼容传统的C struct的,在需要与C语言交接的地方起作用
  • 提升效率。《STL源码剖析》中讲到,如果一个数组的成员是POD型别的数据,那么在对该数组进行拷贝操作的时候,可以直接调用更为底层的函数进行操作,而不需要对每一个成员使用复制赋值构造函数
  • 一个POD型别的类或结构体通过二进制拷贝后还能保持其数据不变

 

posted @ 2020-06-19 10:42  浩楠honer  阅读(723)  评论(0编辑  收藏  举报