11.2 Properties of classes
1
平凡可复制类是一个类,它:
(1.1)
- 至少有一个可用的拷贝构造函数,移动构造函数,拷贝赋值运算符或移动赋值运算符([[special]](),[[class.copy.ctor]](),[[class.copy.assign]]()),
(1.2)
- 其中每个可用的拷贝构造函数,移动构造函数,拷贝赋值运算符和移动赋值运算符都是平凡的,并且
(1.3)
- 有平凡的、非删除的析构函数([[class.dtor]]())
2
平凡类是平凡可复制类,并且有一个或多个可用的默认构造函数([[class.default.ctor]]()),所有这些默认构造函数都是平凡的
【注:一个平凡可复制类或平凡类是没有虚函数或虚基类的。——注释结束】
【注:一个平凡可复制类或平凡类是没有虚函数或虚基类的。——注释结束】
3
类S是标准布局类,当它:
(3.1)
- 没有非标准布局类类型的非静态数据成员(或者非标准布局类类型数组),没有引用类型的成员
(3.2)
- 没有[虚函数]()以及[虚基类]()
(3.3)
- 所有非静态数据成员的[访问控制]()必须相同
(3.4)
- 没有非标准布局的基类(所有基类都是标准布局类)
(3.5)
- 每个基类类型至多有一个子对象
(3.6)
- 所有非静态数据成员和位域必须在同一个类中首次声明,要么在都基类中,要么在都派生类中,不可分散
(3.7)
- S的基类不能是集合`M(S)`中元素的类型,对于类型`X`来说,`M(X)`定义如下:
【注:`M(X)`是所有可以在`X`的0偏移量处存在的所有非基类子对象的类型的集合——注释结束】
- (3.7.1) 如果`X`是一个没有(包括可能继承来的)非静态数据成员的非联合体类类型,那么集合`M(X)`为空
- (3.7.2) 如果`X`是一个非联合体类类型,且具有类型为`X₀`的非静态数据成员,该成员要么是零大小的,要么是`X`的第一个非静态数据成员(该成员可以是匿名联合体),则集合`M(X)`由`X₀`和`M(X₀)`的元素组成。
- (3.7.3) 如果`X`是一个联合体类型,则集合`M(X)`是所有`M(Uᵢ)`的并集以及包含所有`Uᵢ`的集合的并集,其中每个`Uᵢ`是`X`的第i个非静态数据成员的类型。
- (3.7.4) 如果`X`是元素类型为`Xₑ`的数组类型,则集合`M(X)`由`Xₑ`和`M(Xₑ)`的元素组成。
- (3.7.5) 如果`X`是非类、非数组类型,则集合`M(X)`为空。
Paw5zx个人理解:点击查看更多
M(X)是所有可以在X的0偏移量处存在的所有非基类子对象的类型的集合
如果某个类型T属于M(S),又做为S的基类出现,那么S不是标准布局类。
(3.7.2) 如果
X是一个非联合体类类型,且具有类型为X₀的非静态数据成员,该成员要么是零大小的,要么是X的第一个非静态数据成员(该成员可以是匿名联合体),则集合M(X)由X₀和M(X₀)的元素组成。
应该是凡是满足条件的成员类型都要计入,即当X里存在多个满足条件的成员类型时,分别对每个这样的X₀应用一次并集。
Paw5zx个人理解:点击查看更多
3.7总结:
不存在与以下各项具有相同类型的基类子对象:
- 对于非联合类型,为其首个非静态数据成员(见空基类优化),递归地,若该数据成员具有非联合类类型,则为其首个数据成员,或者若该数据成员为联合体类型,则为其所有非静态数据成员,或者若该数据成员为数组类型,则为其元素类型,依此递归。
- 对于联合体类型,为其任何非静态数据成员,递归地,若该数据成员具有非联合类类型,则为其首个数据成员,或者若该数据成员为联合体类型,则为其所有非静态数据成员,或者若该数据成员为数组类型,则为其元素类型,依此递归。
- 对于数组类型,为数组元素的类型,递归地,若该数据成员拥有非联合类类型,则为其首个数据成员,或者若该数据成员为联合体类型,则为其所有非静态数据成员,或者若该数据成员为数组类型,则为其元素类型,依此递归。
4
【示例:
——示例结束】
1 | struct B { int i; }; // standard-layout class |
Paw5zx个人理解:点击查看更多
可以根据以下示例验证上述规则
1 |
5
标准布局结构体是使用struct或class关键字定义的标准布局类。标准布局联合体是使用union关键字定义的标准布局类。
6
【注:标准布局类在与用其他编程语言编写的代码进行交互时非常有用。它们的内存布局在[[class.mem]]()中有明确规定。——注释结束】
7
【示例:
——示例结束】
1 | struct N { // neither trivial nor standard-layout |
8
【注:类类型的聚合体(`Aggregates`)在[[dcl.init.aggr]]()中进行了描述。——注释结束】
9
若类S是一个聚合体,或者它至少拥有一个平凡的、可用的构造函数和一个平凡的、未被删除的析构函数,那么类`S`就是一个隐式生命周期类(`implicit-lifetime class`)
这确保了具有相同类类型并且属于同一个最派生对象的两个子对象不会被分配在相同的地址。