构造函数、'=' 赋值运算符和析构函数(高级,第 #2 部分)
- 来源: https://www.freebasic.net/wiki/wikka.php?wakka=ProPgCtorsAssignDtors2
- 最后更新: 2023-09-21
正确使用构造函数、'=' 赋值运算符和析构函数,它们是用于构造/初始化、赋值和销毁对象的特殊成员过程(第 #2 部分)。
目录
1. 编译器交互(与默认构造函数、复制构造函数和复制赋值运算符)
2. 良好规范的规则(对于构造函数、复制构造函数、复制赋值运算符和析构函数)
3. 成员访问权限影响(对构造函数、复制构造函数、复制赋值运算符和析构函数的声明)
1. 编译器交互(与默认构造函数、复制构造函数和复制赋值运算符)
在赋值或复制构造期间,编译器与不同的复制赋值运算符、默认构造函数和复制构造函数调用进行交互,以优化结果对象的复制,尽可能充分利用用户提供的成员过程(可能不全面)。
- 对于赋值('object2 = object1')
- 对于复制构造('Dim As typename object2 = object1')
注意:
如果以下命题之一成立,则 Type 有默认构造函数:
它有显式默认构造函数
其字段中至少有一个有初始化器。
其成员中至少有一个是本身具有默认构造函数的对象。
其 Base(如果存在)有默认构造函数(内置的 'Object' 有默认构造函数)。
在某些条件下,可以为复制构造调用显式复制赋值运算符(如果没有显式复制构造函数)。
但反过来,无论条件如何,显式复制构造函数永远不会为赋值调用(如果没有显式复制赋值运算符)。
- 在赋值或复制构造期间,用于突出显示/验证与默认构造函数、复制构造函数和复制赋值运算符的此交互的示例
2. 良好规范的规则(对于构造函数、复制构造函数、复制赋值运算符和析构函数)
影响构造函数、复制构造函数、复制赋值运算符和析构函数行为的提醒:
定义显式默认构造函数会替换编译器构建的隐式默认构造函数。
定义除默认构造函数以外的显式构造函数会取消编译器构建的隐式默认构造函数。在这种精确情况下,根本没有默认构造函数!
编译器构建的隐式复制构造函数(或复制赋值运算符,或析构函数)可以被用户定义的显式复制构造函数(或复制赋值运算符,或析构函数)替换。
但(与默认构造函数相反),始终有一个复制构造函数(或复制赋值运算符或析构函数),要么是编译器构建的隐式的,要么是用户定义的显式的。
当有对象组合时,被组合的对象 Type 必须有与复合对象声明匹配的隐式或显式构造函数。
当有 Type 继承时,被继承的 Type 必须有默认的隐式或显式构造函数(除非继承 Type 有用户显式定义的常量复制构造函数),即使没有构造任何对象(编译器仅对继承结构进行测试)。此行为似乎是 FreeBASIC 特有的。
从以上所有内容,可以推导出避免大多数编译错误和运行时 bug 的"黄金规则"。
更安全代码的黄金规则(在编译时和运行时):
如果用户显式定义任何构造函数,强烈建议同时也显式定义默认构造函数。
如果用户需要显式定义(具有非空主体的)复制构造函数或复制赋值运算符或析构函数,最好同时定义这 3 个(众所周知的"三法则"),外加默认构造函数(上述规则也适用)。
从以上所有内容及更具体的情况(带继承),可以提出一条"最大化规则",使操作更加安全。
非常安全代码的最大化规则(在编译时和运行时),但有时会最大化实际约束:
- 如果用户需要显式定义任何形式的构造函数过程(包括任何形式的复制构造函数)或任何形式的 let 运算符过程或析构函数过程,强烈建议同时定义默认构造函数、标准复制构造函数、标准 let 运算符和析构函数。
(这 4 个显式过程被显式定义,以始终正确重载编译器的相应隐式操作)
- 应用良好规范规则的示例
3. 成员访问权限影响(对构造函数、复制构造函数、复制赋值运算符和析构函数的声明)
在声明此类成员过程时可以应用访问权限,以禁止用户执行某些特定命令(从 Type 外部)。
默认构造函数、复制构造函数、复制赋值运算符和析构函数是唯一可以由编译器构建隐式版本的成员过程。
因此,如果要禁止用户从 Type 外部访问它们,在声明时有必要用具有受限访问权限(非 Public)的显式版本来重载它们。此外,如果在程序中实际上从未调用此类成员过程,它们可以没有实现(没有主体定义)。
- 示例 - 必须禁止任何基对象构造的继承结构:
- 示例 - 单例结构(任意时刻最多只能存在一个对象):
参见
Constructor,DestructorOperator =[>] (Assignment),Operator Let (Assignment)- 构造函数和析构函数(基础)
- 构造函数、'=' 赋值运算符和析构函数(高级,第 #1 部分)
返回 目录