-
此题训练点是运算符
+
与*
的重载 -
但我想借用此题浅浅谈一下我对“返回类型”&“内存释放”的理解
-
首先先贴一个大家可能都会这么写的AC代码
#include <iostream>
using namespace std;
class Complex
{
private:
int real, image;
public:
Complex(int real = 0, int image = 0) :real(real), image(image) {}
~Complex() {}
Complex operator+ (Complex& obj)
{
Complex* ans = new Complex(real + obj.real, image + obj.image);
return *ans;
}
Complex operator* (Complex& obj)
{
Complex* ans = new Complex(real * obj.real - image * obj.image,
real * obj.image + image * obj.real);
return *ans;
}
void Output()
{
cout << real << ((image >= 0) ? "+" : "") << image << "i" << endl;
}
};
int main()
{
int x, xx, y, yy, z, zz;
cin >> x >> xx >> y >> yy >> z >> zz;
Complex a(x, xx), b(y, yy), c(z, zz);
Complex res = a + b + c;
// 等价于 Complex res = a.operator+(b).operator+(c);
Complex ans = a * b * c;
// 等价于 Complex ans = a.operator*(b).operator*(c);
res.Output();
ans.Output();
return 0;
}
-
由于上述程序重载
+
与重载*
形式完全一致,故以下仅以重载+
为例注意观察:函数左上角声明的
返回值类型
与函数最后一行的return
语句
-
返回值类型:对象
return内容:对象
Complex operator+ (Complex& obj) { Complex* ans = new Complex(real + obj.real, image + obj.image); return *ans; }
-
返回值类型:对象的引用
return内容:对象
Complex& operator+ (Complex& obj) { Complex* ans = new Complex(real + obj.real, image + obj.image); return *ans; }
可以看到两者的return的内容完全一致,仅仅在左上角声明的返回值类型
不同
-
对于上述第一种,主函数调用时写成
Complex res = a + b + c;
-
对于上述第二种,主函数调用时写成
Complex& res = a + b + c;
可以看到两者的唯一区别也仅仅是一个&
运算符
- 那么区别在哪呢
-
第一种写法,不需要手动释放空间,系统会自动执行析构函数,释放掉主函数中的
Complex a, b, c, res
而第二种写法,系统同样会自动执行析构函数,释放掉主函数中的
Complex a, b, c
但是主函数中申请出来的
res
需要手动释放delete& res
-
返回对象会触发复制构造函数,此程序没写,故默认触发;而返回对象引用不会。因此,返回对象引用比返回对象更高效
-
对于返回对象,如果这个对象在函数中是以局部变量的形式存在的话(对象的存在形式有两种),
- 一种就是上面AC代码的写法,自己申请的空间,在堆里;
- 一种就是直接作为局部变量,在栈里;
那么,首先这个对象被返回之后,系统将自动销毁这个局部变量,其次被返回的这个对象是左值,即无法改变的常量
而返回对象的引用,在函数中只能是一种存在形式,即自己申请的空间,同样是在堆里,这个引用的对象被返回后,是右值,即可以修改值
(其实对于一个已经存在的变量,是一定要的返回对象的引用的,因为后续对这个变量的操作都将改变这个最原始的变量;而如果此时返回对象的话,触发默认复制构造函数不说,此后对这个变量的操作都将失效,会造成严重的内存泄漏(原来那个存在的变量的空间这辈子都释放不了了)与逻辑错误)
所以
Complex operator+ (Complex& obj) { Complex* ans = new Complex(real + obj.real, image + obj.image); return *ans; }
其实是很不规范的写法hhh,应该要返回引用的hhh
规范化总结3.
-
对于局部变量,只能返回作为左值的对象,否则如果返回这个局部变量的引用,有两种方法会让程序崩溃
- 修改被返回的引用的值(对已经被销毁的东西进行操作显然不行)
- delete这个引用的对象的指针(因为已经被销毁了)
-
对于已经存在的(其实就是堆里的)变量,一定要返回对象的引用
-
好吧,但是返回对象的引用还需要手动释放空间,好麻烦的,有什么自动帮我实现上述功能的语法吗?没错当然有!在C++11中,引入了
智能指针
这么一个概念可以自动实现- 申请空间
- 识别到变量全部操作完后…
- 释放空间
-
于是上述两个重载函数分别可以写成
Complex operator+ (Complex& obj) { unique_ptr<Complex> ans(new Complex(real + obj.real, image + obj.image)); return *ans; }
Complex operator* (Complex& obj) { unique_ptr<Complex> ans(new Complex(real * obj.real - image * obj.image, real * obj.image + image * obj.real)); return *ans; }
-
智能指针包含在头文件
#include <memory>
中 -
在
return
语句执行完之后由于申请出的ans
被识别出已经没有用了,自动执行析构函数,释放内存,是不是很方便hhh -
但是要注意的是,这两个函数一定不能返回引用,因为此处的
ans
变量相当于局部变量了。因为在执行return
语句后,就自动执行析构函数,将这个ans
变量释放掉了
感觉逻辑性好差 😦 希望大家可以读懂并且欢迎指正 Orz
-