a review at operator overloading

定义一个complex类

complex c1 (5, 1);
complex c2 (2);
c2 += c1;
c2 += c1 += c1;

由于我们没有对complex类定义+=操作符,所以这一行会报错。
于是乎我们定义complex类+=的operator overloading

inline complex& complex::operator += (const complex& r) {
  return __doapl(this, r);
}
inline complex& __doapl(complex* ths, const complex& r) {
  ths->re += r.re;
  ths->im += r.im;
  return *ths;
}

这两行代码源于标准库,看似简单,实际很复杂

  1. 为何用inline? 因为当函数短小且不断被多次调用的时候会消耗掉大量的stack,所以要用inline,让编译器把这个函数展开
  2. 为什么+=内部要设计为调用__doapl(this, r)? 不知道,设计问题,照着做就行了,就是要叠一层
  3. c2 += c1的具体调用顺序是如何?
  • 首先编译器读入c2 += c1,识别出+=是一个二元运算符(注意:不是编译器真的预先知道+=代表了二元,而是它找到了c2是左值,+=在中间,c1是右值,所以认定+=是二元操作符),之后去寻找程序中是否有"complex& aaaaaa" "+=" "complex bbbbbb"这样结构的定义
  • 于是接着上文,这两段程序难以理解的地方还有一个this的问题,因为我们这个是包装在complex内的function,假设有不是包装在complex内的function,比如普通加法
inline complex operator+(const complex& x, const complex& y) {
  return complex(real(x) + real(y), image(x) + image(y));
}
complex c1, c2, c3;
c2 = c1 + c2;
  • 这里产生了个疑惑,为什么同为二元运算符,同为加法,这个函数却有x, y两个参数,而 inline complex& complex::operator += (const complex& r)却只有一个呢??

    • 这涉及到this指针的问题
  • 对于inline complex& complex::operator += (const complex& r)函数,编译器会将它认为是inline complex& complex::operator += (this, const complex & r) 因为它是类的成员函数,所以多了一个this,代表这个类的实例本身,所以c2 += c1,本质上还是两个参数,为什么只写一个参数?因为剩下的那个(隐藏的)参数代表自身。

  • 对于上述的+号,执行流程为,识别二元操作符+,去找

  • 那么:c1+=c2+=c1如何理解呢???

    • 因为它的实现是返回一个complex&,假设返回一个void,c2+=c1是可以运行的,但是c2+=c1这个返回值是void,再加到c1就不行了。那么,为什么返回一个complex&而不是complex呢?因为快
  • 问题来了,那为什么inline complex operator+(const complex& x, const complex& y)不return引用呢?这样不也是快么??因为+=后的返回值,是下一次+=的参数,+=到最后,它的最后返回值同样会赋值一个上文已存的complex,这是合法的。inline complex& operator+(const complex& x, const complex& y)不合法,是因为它返回的complex在函数生命周期销毁后,就不具备&了。

  • 有一个例外

inline complex& operator+ (const complex& x) {
   return x;
}

这里标准库没写&,但实际上写了也没事,因为x的生命周期是上文给的
但是对于-号这么写就是错的,如下:

inline complex& operator- (const complex& x) {
  return complex(-real(x), -image(x));
}
  • 还有一个问题:
inline complex conj(const complex &x ) {
  return complex(real(x), -image(x));
}
cout << conj(c1) << endl;
cout << conj(c1) << conj(c2) << endl;

如何重载<<? 答案是这样的

#include <iostream>
ostream& operator << (ostream& os, complex& c1) {
  return os << "image" << c1.image <<  "real" << c1.real;
}
  • 首先这个无法写成类函数,因为cout是一个ostream对象,标准库写好的,我们只能写成global,然后,按照ostream << complex的结构,我们将第一个参数定义为ostream& os, &为了效率,不能加const注意,因为加了const我们就没有办法写入这个ostream了,然后complex& c1。。。。对于连续输出<<,就是一个执行顺序的问题,从左到右,返回ostream&,(不存在ostream&对象在函数内销毁的问题,所以没必要输出ostream)不赘述。

operator overload知识点太杂,这篇博客还是介绍的蛮全的 https://www.cnblogs.com/linuxAndMcu/p/10395383.html

posted @ 2021-01-02 16:36  五个桔核  阅读(63)  评论(0)    收藏  举报