C++运算符重载与无名对象引用问题的一点思考
问题的发现
今天复习C++运算符重载时候的意外发现:关于类的临时对象以及引用的一些思考。来源于C++程序设计(小红书)上给出了“+”号运算符重载的样例。
基本知识回顾
重载运算符的习惯
- C++规定,赋值运算符=、下标运算符[]、函数调用运算符()、成员运算符->必须作为成员函数。
- 流插入运算符<< 和流提取运算符 >>、类型转换运算符不能定义为类的成员函数,只能作为友元函数。
- 一般将单目运算符和复合运算符重载为成员函数
- 一般将双目运算符重载为友元函数。 ## Next So 根据上述的习惯,我对着屏幕敲了书上的样例的类定义的代码。
1 |
|
写完感觉看着是没什么问题对吧(我真觉的没什么问题)。 然而运行了一下就报错了,那...报什么错了?
[Error] no match for 'operator+' (operand types are 'Complex' and 'double')
[Note] candidates are:
[Note] Complex operator+(Complex&, Complex&)
[Note] no known conversion for argument 2 from 'Complex' to 'Complex&'
哦哦看了下报错,原来是重载函数写的问题,我先去查了一下代码。
课本上给出的重载运算符声明和定义是:
friend Complex operator + (Complex a,Complex b);
...
我自己看走眼敲成了:
friend Complex operator + (Complex &a,Complex &b);
...
看出问题在哪了么?没错就是一个"&"的差别,即形参是Complex类对象的引用还是Complex类对象。
问题展开与解决
去掉"&"后程序果然可以正常运行的。不过我考虑了一下程序的实现过程,不对啊,一个简单的相加,我传递一个类对象的引用 不应该出现什么问题啊,"+"本身又不会对引用的对象进行什么操作,返回类型是一个Complex类的对象。问题出现在哪里了?
一些奇葩(正经)的实验
于是乎我把之前写的简单的"+"重载的程序,函数的形参都改为对象的引用,再运行。结果更奇怪了,除了上述程序,都没有报错。 这个程序又独特在哪里了?
问题点
再返回去读一读程序,这个样例用到了转换构造函数。
c3=c1+4.0;
在保留注释掉以后程序果然可以正常运行了,问题确实出现在这里。既然是转换构造函数的问题,隐式调用(默认执行的)方式不可行,那我试一试显式的
c3=c1+Complex(4.0);
然而...报错依旧,只有微小的变化
[Error] no match for 'operator+' (operand types are 'Complex' and 'Complex')
对照之前的
[Error] no match for 'operator+' (operand types are 'Complex' and 'double')
原来最开始的程序并没有成功的调用转换构造函数。编译器没有检测到? 看来是哪里出了什么之前没考虑过的问题。
思考
(不摔桌!不摔桌!冷静!)如果通过引用进行参数传递,可以免去建立实参的拷贝,空间和时间上都可以得到优化, 逻辑推理一下,那这种传参的方式应该被推行啊,相近的例子就是流提取和流插入运算符的重载,第二个参数都是自定义类的引用。
那...为何"+"的重载不采用这种方式呢?看来是有问题的,结合之前的实验,在不出现转换构造函数调用的情况下,形参为引用是行得通的。 看来这个问题和转换构造函数也相关。
再回过头来看报错信息
[Error] no match for 'operator+' (operand types are 'Complex' and 'Complex')
[Note] candidates are:
[Note] Complex operator+(Complex&, Complex&)
[Note] no known conversion for argument 2 from 'Complex' to 'Complex&'
最后一句:Complex到Complex&转换出了问题,这个比较稀奇。从类对象到类对象的引用不应该是顺理成章的事情么,别名而已。Emm问题就在这个别名身上
引用<->别名
转换构造函数出现的无名对象
转换构造函数是构造函数的重载,在使用转换构造函数的过程中,我们可以建立一个有名对象,也可以建立一个无名对象。 书上中的代码样例
Complex c1(3.5); //调用转换构造函数建立对象c1
Complex(3.6); //调用转换构造函数建立无名对象,合法,但无法使用。
就是这个无名对象的锅,我们回过头来看最开始发现的问题点:
c3=c1+Complex(4.0);
由于Complex(4.0)建立的是一个无名对象,而采用我错误写出的以引用作为形参的 "+"运算符重载,会发生什么?没有名字,自然无法引用啊。 这也就解释了报错信息为什么Complex到Complex&转换出了问题。无名的对象无法建立引用(没名字你怎么给它找个别名)。
事后诸葛亮
根据简单的实验其实能得到一些结论 - 涉及转换构造函数的运算符重载,形参都应该是类对象而不是类对象的引用。 - C++自己实现的"="应该也是用类对象作形参,因为 c1=Complex(3.6); 这一句是可以正常执行的。
算作对引用以及构造函数的一个探索吧:)
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!