如何在 C++ 中 "default constructor"
How to "default constructor" in C++
我最近 运行 遇到了一个问题,因为我是一名自学的 C++ 程序员,所以我真的很想知道现实世界中的专业人士是如何解决它的。
为所有 类 编写默认构造函数是个好主意吗?如果您的 类 没有默认构造函数,STL 中是否有某些部分将无法工作?
如果如此,那么如何编写一个默认构造函数来做一些明智的事情呢?也就是说,如果根本没有合理的默认值,我如何为我的私有成员分配默认值?我只能想到两个解决方案:
- 为每个成员使用指针(或 unique_ptrs),这样 nullptr 就意味着该字段未初始化。
或
- 添加额外的 fields/logic/methods 来完成检查字段是否已初始化的工作并依赖它(有点像 unique_ptr 的 "reset" 方法).
人们在现实世界中如何解决此类问题?
Is it a good idea to write a default constructor for all classes?
没有。有时为对象设置默认值是没有意义的。
Aren't there certain parts of the STL that won't work if your classes don't have default constructors?
有些部分需要 DefaultConstructible 对象。并且有一些方法可以规避它(重载需要使用一个对象而不是默认构造的对象)。
如果你的数据类型有一个默认的构造函数没有意义,那么就不要写一个。
(STL 早已过时,但我假设您指的是标准库。)即使包含的类型没有默认构造函数,大多数标准库容器也能正常工作。一些值得注意的问题:
std::vector<T>::resize(n)
要求 T
具有默认构造函数。但是如果没有,您可以使用 erase
和 insert
代替。
std::map<K,V>::operator[]
和 std::unordered_map<K,V>::operator[]
要求 V
具有默认构造函数。但是如果没有,您可以使用 find
和 insert
代替。
Is it a good idea to write a default constructor for all classes?
没有。如果您的类型没有合理的“默认值”,则不应编写默认构造函数。这样做会违反最小意外原则。
也就是说,许多类型 确实有 合理的默认值。
- 数字零(更笼统地说:中性元素)
- 空字符串
- 空列表
- 一个 0 × 0 矩阵
- UTC 时区± 00:00
- …
对于这样的类型,您确实应该定义一个默认构造函数。
其他类型没有自然的默认值,但有一个“空”状态,可以通过执行某些操作达到。默认构造这样的对象以具有该状态是明智的。
- 一个 I/O 流与每个操作失败的任何源/接收器断开连接(可以通过到达文件末尾或遇到 I/O 错误来到达)
- 一个不持有锁的锁守卫(可以通过释放锁到达)
- 不拥有对象的智能指针(可以通过释放被管理对象来达到)
- …
对于这些类型,是否定义默认构造函数是一个权衡。这样做没有坏处,但会使您的类型稍微复杂一些。如果它是在库中找到的 public 类型,那么这可能是值得的。无论如何,如果您打算实现一个移动构造函数(和赋值运算符),您同样可以定义默认构造函数来构造对象,该对象处于离开它可以达到的状态。
对于其他类型,您无法定义合理的默认值。
- 一周中的某一天
- 宝宝的名字
- …
不要为这些类型发明一个人为的“空”状态,只是为了让它们可以默认构造。这样做会使代码复杂化,并迫使您提供不太有用的 class 不变量,因为您可以在没有人工状态的情况下提供。如果有人真的需要那个额外的状态,他们可以很容易地使用 optional<T>
但反过来是不可能的。
Aren't there certain parts of the STL that won't work if your classes don't have default constructors?
是的。 std::vector::resize
可能是最突出的例子。不过不用担心这些。如果您的类型没有合理的默认值,则执行该操作也没有意义。这不是你类型的错。它是您尝试建模的概念的本质所固有的。
我最近 运行 遇到了一个问题,因为我是一名自学的 C++ 程序员,所以我真的很想知道现实世界中的专业人士是如何解决它的。
为所有 类 编写默认构造函数是个好主意吗?如果您的 类 没有默认构造函数,STL 中是否有某些部分将无法工作?
如果如此,那么如何编写一个默认构造函数来做一些明智的事情呢?也就是说,如果根本没有合理的默认值,我如何为我的私有成员分配默认值?我只能想到两个解决方案:
- 为每个成员使用指针(或 unique_ptrs),这样 nullptr 就意味着该字段未初始化。
或
- 添加额外的 fields/logic/methods 来完成检查字段是否已初始化的工作并依赖它(有点像 unique_ptr 的 "reset" 方法).
人们在现实世界中如何解决此类问题?
Is it a good idea to write a default constructor for all classes?
没有。有时为对象设置默认值是没有意义的。
Aren't there certain parts of the STL that won't work if your classes don't have default constructors?
有些部分需要 DefaultConstructible 对象。并且有一些方法可以规避它(重载需要使用一个对象而不是默认构造的对象)。
如果你的数据类型有一个默认的构造函数没有意义,那么就不要写一个。
(STL 早已过时,但我假设您指的是标准库。)即使包含的类型没有默认构造函数,大多数标准库容器也能正常工作。一些值得注意的问题:
std::vector<T>::resize(n)
要求T
具有默认构造函数。但是如果没有,您可以使用erase
和insert
代替。std::map<K,V>::operator[]
和std::unordered_map<K,V>::operator[]
要求V
具有默认构造函数。但是如果没有,您可以使用find
和insert
代替。
Is it a good idea to write a default constructor for all classes?
没有。如果您的类型没有合理的“默认值”,则不应编写默认构造函数。这样做会违反最小意外原则。
也就是说,许多类型 确实有 合理的默认值。
- 数字零(更笼统地说:中性元素)
- 空字符串
- 空列表
- 一个 0 × 0 矩阵
- UTC 时区± 00:00
- …
对于这样的类型,您确实应该定义一个默认构造函数。
其他类型没有自然的默认值,但有一个“空”状态,可以通过执行某些操作达到。默认构造这样的对象以具有该状态是明智的。
- 一个 I/O 流与每个操作失败的任何源/接收器断开连接(可以通过到达文件末尾或遇到 I/O 错误来到达)
- 一个不持有锁的锁守卫(可以通过释放锁到达)
- 不拥有对象的智能指针(可以通过释放被管理对象来达到)
- …
对于这些类型,是否定义默认构造函数是一个权衡。这样做没有坏处,但会使您的类型稍微复杂一些。如果它是在库中找到的 public 类型,那么这可能是值得的。无论如何,如果您打算实现一个移动构造函数(和赋值运算符),您同样可以定义默认构造函数来构造对象,该对象处于离开它可以达到的状态。
对于其他类型,您无法定义合理的默认值。
- 一周中的某一天
- 宝宝的名字
- …
不要为这些类型发明一个人为的“空”状态,只是为了让它们可以默认构造。这样做会使代码复杂化,并迫使您提供不太有用的 class 不变量,因为您可以在没有人工状态的情况下提供。如果有人真的需要那个额外的状态,他们可以很容易地使用 optional<T>
但反过来是不可能的。
Aren't there certain parts of the STL that won't work if your classes don't have default constructors?
是的。 std::vector::resize
可能是最突出的例子。不过不用担心这些。如果您的类型没有合理的默认值,则执行该操作也没有意义。这不是你类型的错。它是您尝试建模的概念的本质所固有的。