拷贝构造/赋值函数的参数: 为什么我的拷贝函数报错?#
拷贝函数用 Widget(Widget const& other) 和 operator=(Widget const& other).
本解答是很多人用 & 作为参数出错而提问, 我忍无可忍的结果.
为什么不是 Widget(Widget other) 和 operator=(Widget other)?#
定义拷贝构造函数的目的是什么? 是为了描述 Widget 对象如何进行拷贝.
当用 Widget(Widget other) 时, 我们就是在说, Widget 先进行拷贝传参, 传入参数后进行拷贝. 拷贝传参也是在拷贝 Widget, 所以我们要为了拷贝传参这次拷贝进行拷贝传参, 传入参数后进行拷贝……
从前有座山, 山里有座庙, 庙里有个老和尚在讲故事, 讲的是从前有座山, 山里有座庙, 庙里有个老和尚在讲故事……
发生无限的递归.
Widget(Widget other) 带来的递归问题. (以前做的图, 用语不太一样)#
为什么不是 Widget(Widget& other) 和 operator=(Widget& other)?#
拷贝只是读取数据, 不会修改 other#
按照拷贝的逻辑, 我们是从 other 中读取数据, 它本身的数据不应该发生任何变化, 为了避免被修改, 应该使用 Widget const& 传递参数. 将 other 作为只读参数, 这称为输入参数.
Widget& 被意外修改# 1class Widget {
2 public:
3 Widget(Widget& other) {
4 /*...*/
5 --other.value_; // 意外被修改!
6 }
7
8 private:
9 int value_;
10};
Widget& 只能引用左值, 但很多情况需要用右值拷贝#
前置内容
Widget& 报错# 1class Widget {
2 public:
3 Widget(Widget& other);
4};
5
6Widget function();
7
8int main() {
9 Widget widget = function(); // 错误: function 返回了一个临时对象, 不能被引用而用于拷贝!
10}
Widget const& 正常运行# 1class Widget {
2 public:
3 Widget(Widget const& other);
4};
5
6Widget function();
7
8int main() {
9 Widget widget = function(); // 正确!
10}
危险
以上内容是针对 C++98~C++23 的泛用解答, 随着版本变化, 很多细节会有差异.
事实上很多解答都存在这样的问题, 因为教学使用的 C++98 太过时了, VS 2017 默认版本是 C++14, gcc 11 和 clang 16 默认版本是 C++17.
这里引起的争议比较大, 所以免责声明一下.