C++标准定义了当类具有平凡构造函数和/或平凡析构函数时的一些非常具体的行为。
例如,根据标准的§3.8/1:
一个类型为T的对象的生命周期在以下情况下结束: - 如果T是一个具有非平凡析构函数(12.4)的类类型,则析构函数调用开始,或 - 对象占用的存储被重用或释放。
因此,
- 如果一个对象不是平凡可销毁的,则在析构函数调用后尝试访问对象成员是未定义的行为。 - 如果一个对象是平凡可销毁的,则在析构函数调用后尝试访问对象成员是安全的,不会导致未定义的行为。
尽管这个例子可能不是最好的例子,但它显示出了一个对象是否是平凡可销毁的差异可能是关键的(UB / non-UB)。
标准的§12.4/3规定(总结):如果类T的析构函数是隐式定义的、非虚拟的,并且类T的所有基类和成员都是平凡销毁的,则T的析构函数是平凡的。
就我(谦虚)的经验而言,我从未看到过编译器生成的代码方面的区别,包括:
- 具有平凡默认构造函数和/或平凡析构函数的类,和 - 具有用户定义的空构造函数和/或非虚拟的用户定义的空析构函数的类(只要类、其基类和成员类也具有非虚拟析构函数用户定义的空或平凡)。
因此,我的问题是:
- 用户定义的空构造函数/析构函数在编译器代码生成、优化、权衡等方面如何被认为是类似于平凡构造函数/析构函数。 - 相同的问题与用户定义的非空构造函数/析构函数;一个在构造函数/析构函数中实现的代码应该遵循什么规则才能将它们视为类似于平凡的。
我的问题与标准无关(请不要回答标准规定了什么是平凡构造函数/析构函数,因此用户定义的构造函数/析构函数不是),而是与编译器处理用户定义的构造函数/析构函数的方式以及编译后代码的行为如何改变(或不改变)相关。
例如,根据标准的§3.8/1:
一个类型为T的对象的生命周期在以下情况下结束: - 如果T是一个具有非平凡析构函数(12.4)的类类型,则析构函数调用开始,或 - 对象占用的存储被重用或释放。
因此,
- 如果一个对象不是平凡可销毁的,则在析构函数调用后尝试访问对象成员是未定义的行为。 - 如果一个对象是平凡可销毁的,则在析构函数调用后尝试访问对象成员是安全的,不会导致未定义的行为。
尽管这个例子可能不是最好的例子,但它显示出了一个对象是否是平凡可销毁的差异可能是关键的(UB / non-UB)。
标准的§12.4/3规定(总结):如果类T的析构函数是隐式定义的、非虚拟的,并且类T的所有基类和成员都是平凡销毁的,则T的析构函数是平凡的。
就我(谦虚)的经验而言,我从未看到过编译器生成的代码方面的区别,包括:
- 具有平凡默认构造函数和/或平凡析构函数的类,和 - 具有用户定义的空构造函数和/或非虚拟的用户定义的空析构函数的类(只要类、其基类和成员类也具有非虚拟析构函数用户定义的空或平凡)。
因此,我的问题是:
- 用户定义的空构造函数/析构函数在编译器代码生成、优化、权衡等方面如何被认为是类似于平凡构造函数/析构函数。 - 相同的问题与用户定义的非空构造函数/析构函数;一个在构造函数/析构函数中实现的代码应该遵循什么规则才能将它们视为类似于平凡的。
我的问题与标准无关(请不要回答标准规定了什么是平凡构造函数/析构函数,因此用户定义的构造函数/析构函数不是),而是与编译器处理用户定义的构造函数/析构函数的方式以及编译后代码的行为如何改变(或不改变)相关。