(由于简书目前不支持[toc]无法快速生成目录,所以可能看起来会有点长。)
这篇文章的参考处:
https://stackoverflow.com/questions/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special
概述
本段分三个部分讲,从最开始的C++03一直到目前的C++17。
首先要明白一个从属关系和这两个概念的基本:
聚类是PODs的一个超集,即如果一个class(在标准中,术语class代表了class,struct,union)是PODs,那么这个类就必为聚类,但是如果一个class是聚类,这个class不一定是一个PODs。
那么首先要明白的是,聚类的一个最明显的特性,一个聚类要求,其本身实例的结构和内部定义的结构一致,即和C中的struct一样,其实实际上是异常相似,里面的成员是什么,那么相应一个实例的内部构造就是这些东西组成的,并且要求以上说道的这些成员是语法可见的
开始正题:
C++03
一、
一个聚类由本身特性可知,聚类是不可能有多态的,因为如果存在,则必须在实例内保留一个指针指向虚表以确定正确执行当前实例动态类型的函数
二、
以任意访问限定存在的成员函数,静态数据成员都是允许的,因为以上两者不会影响对象实例的内部结构
三、
聚类不能有用户定义的任何形式的构造函数(以下统称构造函数),即移动,拷贝,构造,等,(下面是C++11的内容扩展)但是用户可以显示指定出来,即显示说明构造函数的访问限定(在构造函数后加=default)
以上是PODs和聚类相同的地方。
不同的地方有:
一、
聚类可以有用户定义的复制函数,析构函数
二、
聚类的非静态成员可以是非PODs的类型(包括数组,只要是一个数组,就是一个聚类)
PODs
一、
PODs要求,用户不能自定义复制函数,析构函数,并且非静态成员成员必须在满足聚类前提下不能是非PODs类型,以及引用类型
以上是C++03的聚类和POD的定义,下面来看一下特点,示例请在https://github.com/KinoluKaslana/CPPLearning/blob/master/Aggregate_and_PODs.cpp中查看
首先是聚类
一、
从数组说起,其初始化可以通过一个{}进行初始化(即进行显式初始化),当initlizer_list中的数量等于数组容量时,将对其进行等值拷贝,如果存在小于的情况,那么其余没有被值包括到的数据就会被按照默认的初始化方式进行初始化,对于表两类型,其会被初始化为0,对于成员类元素则是调用其默认构造函数,但是如果成员是引用,则无法对其进行默认初始化
二、
对于其他的聚类也一样可以通过一对{}将其成员数据直接初始化,没有被包括的则按照默认初始化,如果类数据成员和引用不存在直接初始化数据(对于类数据则需要传入相应构造函数的数据格式)则会抛出错误
注意,以上显示初始化并不走任何的构造函数
C++11
C++11较C++03
一、
对聚类的变化不大,只是对C++11中的新特性——在定义时类成员初始化做了限制,当一个类,存在非静态的类内成员初始化,那么这个类就不是一个聚类
二、
对描述:不能有用户声明的构造函数;改为->不能有用户提供功能的构造函数
但是对于POD则进行了极大的改动,并且将其拆为了两个更加实用的两个类:
trivial和standard layout
trivial:
要了解这个类,就必须了解什么是trivial和non-trivial
如果一个类型符合以下规定:
一、
没有用户提供的析构,复制,移动构造函数(赋值运算符),允许继承或被继承但没有虚函数,虚基类
二、
对于所有的数据成员,基类数据成员,作为数据成员的数组的元素类型,必须递归的满足上述条件
三、
对于构造函数,则同样满足之前Aggregate的规则同样也不能存在非静态数据成员的花括号,等号的初始化。
四、
对于析构函数,也满足不能是虚函数条件
那么这个类型就是一个trivial copyable的类型
所有的平凡约定,均代表,默认,即非用户提供的
注意:
一)
trivially copyable是trivial的超集,前者不对默认构造函数有要求,后者对默认构造函数有要求,但是不对其他非移动,复制的构造之外的构造函数有限制。
二)
对于standard layout 的类,不对任何构造函数,析构函数,复制、移动的构造函数,复制、移动的复制运算符做要求,同时,只要求所有的非静态数据成员不为非standard layout类型,和标量类型,同时不能有虚基类,虚函数,多继承是被允许的,对于所有的数据成员,并不做任何访问限制要求,唯一的规定是所有的数据成员必须是同一个访问限制的。
注意:
由于trivially copyable是不对任何默认构造函数有要求的,所以单独只能在trivially copyable以及std layout中使用member interlized对POD系成员进行初始化。
C++14
C++14中仅仅对aggregate做了一个小修改:
一、
允许使用成员初始化。
那么到现在各部分的定义就为:
aggregate:
一、
不允许当前将被定义为aggregate的类(以下称之为该类)在任何位置存在任何虚函数。
二、
该类不允许出现非public的非静态成员
三、
成员函数可以为任意访问限定
四、
不允许有用户提供的构造函数,但是允许有用户提供的重载赋值运算符,析构函数,同样,这一条定义也必须符合前几条的规定
五、
允许基类,非静态数据成员为任意类型,即非aggregate的都可以,并且允许其有成员初始化。
trivial
trivially cpoyable
如果一个类是trivially copyable的话,则需要遵守以下规则:
一、
不允许出现非trivial(即用户提供的)复制、移动构造,重载赋值运算符,析构函数
二、
其可以继承,但不允许出现虚基类,虚函数
三
所有的非静态数据成员,基类必须递归满足上述条件即trivially copyable的
trivial
如果一个类是trivial的话,需要遵守以下规则:
一、
必须满足所有的trivially copyable的规定
二、
默认构造函数必须为平凡,即不允许出现成员初始化。
三、
对所有的trivial成员,基类,必须递归满足上述两条。
standard layout
如果一个类是standard layout的话,需要遵守以下规则:
一、
所有的非静态数据成员必须是同一个访问限定,并且不能是非standard layout的,同时第一个非静态数据成员不能是基类类型
二、
不能有虚基类,虚函数。
三、
继承树上,只允许有一成员存在非静态数据成员
四、
对于所有基类,数据成员递归检测上述约定不能存在菱形继承。
C++17:
aggregate
一、
在C++14的基础上,允许aggregate存在继承,但是继承必须是非虚,非private,protected,并不强制要求基类为aggregate
二、
不允许存在继承构造,explicit的构造函数
三、
如果成员,基类为非aggregate那么他们仍然是list-initialized,此时调用的是相应参数的构造函数,如果不存在,则抛出错误。
trivial
trivially copyable:
对C++14中明确规定的必须包含非trivial的复制,移动构造,运算符重载进行重申——一个要求为trivial的类至少包含一个为非删除的其中以上的所有函数,并且必须包含一个trivial的非析构函数,对于其成员类型,以上函数被声明为删除是会影响当前类的trivially copyable的特性的。
trivial:
同上,在满足上述trivially copyable的情况下,也必须包含一个trivial的,非删除的默认构造函数
standard layout:
C++17对standard layout的继承,有了更加严格的定义:
一、
当存在第一个数据成员为以下类型时,其对应的基类类型不能是:
二、
数据成员为X,其不含有非静态数据成员,那么基类类型的限制集为空集
三、
数据成员为X,其中第一个非静态数据成员类型为X0(有可能是一个匿名union)那么此时基类的限制类型为X0和其X0的元素(如果存在的话)组成
四、
数据成员为X,X是一个union,那么基类限制集为X中所有的类型,并且对其union的类型的成员进行递归本定义的所有条款。
五、
数据成员为X,X是类型为X1的数组那么集合为X1和X1的类型集合组成,该条规定递归本定义中所有条款
六、
数据成员不X,为类,不为数组类型,那么集合为空。