5 操作符重载与临时对象

5 操作符重载与临时对象

operator overloading 操作符重载-1, 成员函数 this

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

inline complex&
complex::operator += (const complex& r)
{
    return __doapl(this, r);
}
inline complex&
complex::operator += (this, const complex& r)
{
    return __doapl(this, r);
}

所有的成员函数一定带着一个隐藏的参数 this 指针, 我们写代码不必写也不能写这个参数, 因为一写就会报错

但是你在函数里面可以用它 return __doapl(this, r);

return by reference 语法分析

传递者无需知道接受者是以 reference 形式接收

你可能会对这个写法感到疑惑

inline complex&
__doapl(complex* ths, const complex& r)
{
    // ...
    return *ths;
}

这个是上一节说的加的那个过程

返回的时候是返回的指针指的一个东西, 然后函数上面写的是返回的一个引用, 你返回的是一个 value, 接收方接收的是引用, 这个是可以的, 因为传递者无需知道接受者是以引用的形式接收

class body 之外的各种定义 (definitions)

现在我们讲一下头文件布局的类-定义部分

inline double
imag(const complex& x)
{
    return x.imag();
}

inline double
real(const complex& x)
{
    return x.real();
}

取得某一个复数的虚部和实部

operator overloading (操作符重载-2, 非成员函数) (无 this)

为了对付 client 的三种可能用法, 这里对应开发三个函数

inline complex
operator + (const complex& x, const complex& y)
{
    return complex(real(x) + real(y), imag(x) + imag(y));
}

inline complex
operator + (const complex& x, double y)
{
    return complex(real(x) + y, imag(x));
}

inline complex
operator + (double x, const complex& y)
{
    return complex(x + real(y), imag(y));
}

下面是使用这些函数, 顺序一一对应

{
    complex c1(2, 1);
    complex c2;

    c2 = c1 + c2;
    c2 = c1 + 5;
    c2 = 7 + c1;
}

因为数学上告诉我们, 一个复数可以和一个实数加在一起

写了好几个函数的话, 编译器就会去找对应的函数

和前面的不同之处在于, 这个没有指针, 前面的有指针

temp onject (临时对象) typename();

还是上面的三个函数

他们返回的不是引用, 而是值 value, 因为他们返回的是 local object

先前的例子是把右边加到左边身上 +=, 结果放到左边去; 这个是相加并没有说放到哪里, 所以势必要在这个函数里面创建一块地方出来, 放这个结果

那这种东西会随着函数的结束而被释放, 所以就只能传值出去, 因为一传引用, 函数释放了, 变量也释放了, 外面想要用的话就没得用了

刚刚是说应该在函数里面创建一个东西来保存结果的, 但是代码里面好像并没有 ?

这里有一个特殊语法, return complex(real(x) + real(y), imag(x) + imag(y));, 一个类的名称, 也就是 typename, 直接在后面加上一个小括号 (), 这个就是创建临时对象

临时对象就是临时要的, 不想给它名称, 到了代码下一行就死了

这种特殊语法一般人可能很少用, 但是在标准库里面经常用

operator overloading (操作符重载), 非成员函数

inline bool
operator == (const complex& x, const complex& y)
{
    return real(x) == real(y) && imag(x) == imag(y);
}

inline bool
operator == (const complex& x, double y)
{
    return real(x) == y && imag(x) == 0;
}

inline bool
operator == (double x, const complex& y)
{
    return x == real(y) && imag(y) == 0;
}

下面是用法举例

{
    complex c1(2, 1);
    complex c2;

    cout << (c1 == c2);
    cout << (c1 == 2);
    cout << (0 == c2);
}
// 共轭复数
inline complex
conj(const complex& x)
{
    return complex(real(x), -imag(x));
}

#include <iostream.h>
ostream&
operator << (ostream& os, const complex& x)
{
    return os << '(' << real(x) << ',' << imag(x) << ')';
}

<< 运算符重载, 现在只能用全局的写法, 非成员函数

如果把这个返回类型改成 void 会怎么样 ?

一般的输出是没有问题的, 因为你是直接扔在了屏幕上, 但是像下面这种连续输出的就不行了

cout << c1 << conj(c1); 先输出 c1, 得到的结果还要继续能够接受 conj(c1)

总结

设计一个 class 要注意什么事情 ?

构造函数的 initialization list 一定要会用

函数该不该加 const

参数的传递尽量考虑传递引用

返回的时候考虑返回引用

数据尽可能放到 private 里面

函数大部分放在 public