# 第8周-任务1-方案3-复数类中运算符重载（与实数运算）

+关注继续查看

本文在方案2的基础上，扩展+、-、*、/运算符的功能，使之能与double型数据进行运算。设Complex c; double d; c?d和d?c的结果为“将d视为实部为d的复数同c运算”的结果（其中?为+、-、*、/之一）。另外，再定义一目运算符 -，-c相当于0-c。

【讲解视频】

【参考解答】

在下面的解答中，我将所有二目运算符的重载定义为友元函数，一目运算符重载为成员函数，这是惯用的做法。

#include <iostream>
using namespace std;
class Complex
{
public:
Complex(){real=0;imag=0;}
Complex(double r,double i){real=r;imag=i;}
Complex operator-();
friend Complex operator+(Complex &c1, Complex &c2);
friend Complex operator+(double d1, Complex &c2);
friend Complex operator+(Complex &c1, double d2);
friend Complex operator-(Complex &c1, Complex &c2);
friend Complex operator-(double d1, Complex &c2);
friend Complex operator-(Complex &c1, double d2);
friend Complex operator*(Complex &c1, Complex &c2);
friend Complex operator*(double d1, Complex &c2);
friend Complex operator*(Complex &c1, double d2);
friend Complex operator/(Complex &c1, Complex &c2);
friend Complex operator/(double d1, Complex &c2);
friend Complex operator/(Complex &c1, double d2);
void display();
private:
double real;
double imag;
};

Complex Complex::operator-()
{
return(0-*this);
}

//复数相加：(a+bi)+(c+di)=(a+c)+(b+d)i.
Complex operator+(Complex &c1, Complex &c2)
{
Complex c;
c.real=c1.real+c2.real;
c.imag=c1.imag+c2.imag;
return c;
}
Complex operator+(double d1, Complex &c2)
{
Complex c(d1,0);
return c+c2; //按运算法则计算的确可以，但充分利用已经定义好的代码，既省人力，也避免引入新的错误，但可能机器的效率会不佳
}
Complex operator+(Complex &c1, double d2)
{
Complex c(d2,0);
return c1+c;
}
//复数相减：(a+bi)-(c+di)=(a-c)+(b-d)i.
Complex operator-(Complex &c1, Complex &c2)
{
Complex c;
c.real=c1.real-c2.real;
c.imag=c1.imag-c2.imag;
return c;
}
Complex operator-(double d1, Complex &c2)
{
Complex c(d1,0);
return c-c2;
}
Complex operator-(Complex &c1, double d2)
{
Complex c(d2,0);
return c1-c;
}

Complex operator*(Complex &c1, Complex &c2)
{
Complex c;
c.real=c1.real*c2.real-c1.imag*c2.imag;
c.imag=c1.imag*c2.real+c1.real*c2.imag;
return c;
}
Complex operator*(double d1, Complex &c2)
{
Complex c(d1,0);
return c*c2;
}
Complex operator*(Complex &c1, double d2)
{
Complex c(d2,0);
return c1*c;
}

Complex operator/(Complex &c1, Complex &c2)
{
Complex c;
c.real=(c1.real*c2.real+c1.imag*c2.imag)/(c2.real*c2.real+c2.imag*c2.imag);
c.imag=(c1.imag*c2.real-c1.real*c2.imag)/(c2.real*c2.real+c2.imag*c2.imag);
return c;
}
Complex operator/(double d1, Complex &c2)
{
Complex c(d1,0);
return c/c2;
}
Complex operator/(Complex &c1, double d2)
{
Complex c(d2,0);
return c1/c;
}

void Complex::display()
{
cout<<"("<<real<<","<<imag<<"i)"<<endl;
}

int main()
{
Complex c1(3,4),c2(5,-10),c3;
double d=11;
cout<<"c1="; c1.display();
cout<<"c2="; c2.display();
cout<<"d="<<d<<endl;
cout<<"-c1=";(-c1).display();
c3=c1+c2;
cout<<"c1+c2="; c3.display();
cout<<"c1+d=";	(c1+d).display();
cout<<"d+c1=";	(d+c1).display();
c3=c1-c2;
cout<<"c1-c2="; c3.display();
cout<<"c1-d=";	(c1-d).display();
cout<<"d-c1=";	(d-c1).display();
c3=c1*c2;
cout<<"c1*c2="; c3.display();
cout<<"c1*d=";	(c1*d).display();
cout<<"d*c1=";	(d*c1).display();
c3=c1/c2;
cout<<"c1/c2=";	c3.display();
cout<<"c1/d=";	(c1/d).display();
cout<<"d/c1=";	(d/c1).display();

system("pause");
return 0;
}


【关于参数类型及限定词选择的一点讨论】

我在空间中，看到了leihengxin发的一个贴子：《链接

他给的程序是：

#include<iostream>
using namespace std;
class Complex
{
public:
Complex(){real=0;imag=0;}
Complex(double r,double i){real=r;imag=i;}
friend Complex operator+(Complex &c1,Complex &c2);
friend Complex operator + (Complex &c, double &d);
friend Complex operator + (double &d, Complex &c);
friend void display(Complex &c2);
private:
double real;
double imag;
};

//复数相加：(a+bi)+(c+di)=(a+c)+(b+d)i.
Complex operator+(Complex &c1,Complex &c2)
{
Complex c;
c.real=c1.real+c2.real;
c.imag=c1.imag+c2.imag;
return c;
}
Complex operator+(Complex &c,double  &d)
{
return Complex(c.real+d, c.imag);
}
Complex operator+(double &d, Complex &c)
{
return Complex(c.real+d, c.imag);
}

void display(Complex &c)
{
cout<<"("<<c.real<<","<<c.imag<<"i)"<<endl;
}

int main()
{
Complex c1(3,4),c2(5,-10),c3,c4;
double dd=4.32;
c3=c1+3.14;
cout<<"c1+3.14=";
display(c3);
c4=3.14+c1;
cout<<"3.14+c1=";
display(c4);
system("pause");
return 0;
}


在这个程序中，第X,X行将参数d声明为引用：double &d。这是一个不错的考虑，但也带来了错误，原因在于编译器不能将c1+3.14中3.14这个double型常量与double型引用变量&d匹配起来。至于为什么，请感兴趣的同学能够找资料做些进一步的解释，写在本文的评论中。

如何解决？

有网友给出了一种解决方案：

（1）函数参数不要用引用

friend Complex operator + (Complex &c1, doubled);
friend Complex operator + (double d,Complex &c1);

（2）在main()函数中，声明一个double型变量，如double dd=3.14，然后再执行c3=c1+dd。这能使显然这种方法给重载的运算符的使用套上了枷锁（只能加变量不能加常量，要让复数加一个常量，必然先要赋值给一个变量），并不可取。

（3）将运算符重载函数的参数加上const限定词，为double型的引用const double &d

friend Complex operator + (Complex &c,constdouble &d);
friend Complex operator+ (const double&d, Complex &c);
这样，调用c1+3.14时，编译器顺利地将3.14解释为double型常量。使用这种方案，支持复数加double型变量，而且，当d为变量，在函数中是不允许修改d值，这与“执行加法”的要求是一致的，我们不希望完成操作了，加数也发生了变化。甚至在这里，将参数c也声明为const更好，即

friend Complex operator + (const Complex &c, const double &d);
friend Complex operator+ (const double&d, const Complex &c);

所以，在以上的三种方案中，倾向于用第三种方案。

再讨论一个钻牛角尖的问题，如果采用第三种方案，既能加常量，也能加变量，而且当加变量时，变量值在函数体内是可变的（当然这种设计并不可取），该如何去做。我们可以继续重载operate+函数，一共需要4个版本的定义了：

    friend Complex operator + (const Complex &c, constdouble &d);
friend Complex operator+ (const double&d, const Complex &c);
friend Complex operator + (Complex &c, double &d);
friend Complex operator+ (double &d, Complex &c);

加与不加const是有区别的，编译器认可这是不同的函数，在参数是非const变量时，爱改就改吧。

+关注

1965

0

《2021云上架构与运维峰会演讲合集》

《零基础CSS入门教程》

《零基础HTML入门教程》