C++复数运算的运算符重载

Posted 哈士奇超帅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++复数运算的运算符重载相关的知识,希望对你有一定的参考价值。

C++提供了运算符重载,极大地方便了自定义类型之间的运算,

本文就最简单的复数运算实现让初学者体会运算符重载的作用以及运算符重载的原理。


假设我们有如下复数类

class Complex

private:
	double real;<span style="white-space:pre">			</span>//代表实数部分
	double image;<span style="white-space:pre">			</span>//代表虚数部分
public:

	Complex()real=0;image=0;<span style="white-space:pre">	</span>//默认构造函数<span style="white-space:pre">	</span>
	Complex(double r,double i)<span style="white-space:pre">	</span>//构造函数
	
	real=r;
	image=i;
	

现在我们有两个复数,并想计算和要怎么实现呢?(如下)

Complex sum,c1,c2;
sum=c1+c2;

要在以前,我们是这么计算的:

Complex sum,c1,c2;
sum.real=c1.real+c2.real;
sum.image=c1.image+c2.image;

看起来似乎也不是很复杂,但如果这个类的属性很多,有好几个甚至好几十个呢?这时候C++运算符重载的优势就体现出来了。实现了运算符重载后上述的sum=c1+c2就能直接进行计算了。

实现运算符重载之前我们先要知道其表现形式有两种:重载为成员函数,重载为友元函数

不懂?不要急!我们先从一个简单的函数出发:

Complex operate + (Complex c2)

Complex c;
c.real=this->real+c2.real;
c.image=this->image+c2.image;

注意到,运算符重载的申明形式为:

函数类型 operate 运算符 (形参列表)

...


可以发现这个重载函数将加的运算封装进了Complex类的+运算中使其能支持该类的直接运算。

+ 运算实现两个数相加自然需要两个参数,为何只有c2一个参数呢?

因为上述这段代码将 Complex类的 + 重载为了成员函数,其实它的形参表里隐含了一个this指针,实质上这个成员函数有两个形参(Complex* this,Complex c2),只不过被隐含了。

即该函数能利用this指针直接访问该类的private变量real和image,this指针也可省略不写。

这一规则也就意味着重载为类的成员函数时必须写成静态形式,即在函数类型前面加上 const 关键字,以防止this指针的滥用改变了不该改变的成员变量。

另外,为了有效利用C++的高效,传参时应使用引用而不是,但不在本文讨论范围故不在赘述。

优化后的代码:

const Complex operate + (const Complex &c2)	//第一个const表示函数只读,防止this指针滥用 
						//第二个const防止引用的滥用
Complex c;
c.real=real+c2.real;				//隐含了this指针
c.image=image+c2.image;
return c;




那么这个运算能被重载为友元函数么?什么是友元函数?

先来看这段代码:

friend Complex operator +(const Complex &c1,const Complex &c2)

	return Complex(c1.real+c2.real,c1.image+c2.image);


这是 Complex 类的 + 重载为了友元函数。由于不是成员函数,因此没有隐含的 this 指针,故要把 + 左右两边的参数都传进去,为了使该函数能够访问其他 Complex 对象的成员变量 real 和 image, 必须申明为友元函数。 

声明为友元函数更重要的一个理由:重载一个运算符,使之不再需要用"对象.成员名"的形式调用。参见下表。

该友元函数的形参代表了依自左向右次序排列的各操作数。

为了区分前置后置运算,后置单目运算符++、--的重载,形参表中要增加一个 int,但不必写形参名。


重载为成员函数与重载为友元函数的区别:

运算符成员函数的设计:

运算符使用形式等价式
双目运算符@X1 @ X2X1.operate @ (X2)
前置单目@@ XX1.operate @()
后置单目@X @X1.operate @(0)

运算符友元函数的设计:

运算符使用形式等价式
双目运算符@X1 @ X2operate @ (X1,X2)
前置单目@@ Xoperate @(X1)
后置单目@X @operate @(X1,0)

* 后置单目运算中多出来的0参数仅表示其后置意义。

由上可知,重载为成员函数时:形参个数 = 原操作数个数 -1 (-1为隐含的this指针)(后置++ --除外)  

  重载为友元函数时:形参个数 = 原操作数个数


那么什么时候该重载为成员函数,什么时候该重载为友元函数呢?

为类设计重载操作符的时候,必须选择是将操作符设置为 类成员还是友元函数。在某些情况下,程序员没有选择, 操作符必须是成员;在另一些情况下,有些经验原则可指导我们做出决定。

遵循下列原则:

  1. 赋值(=)、下标([])、调用(())和成员访问箭头(->)等操作符必须定义为成员,将这些操作符定义为非成员函数将在编译时标记为错误。  
  2. 像赋值一样,复合赋值操作符通常应定义为类的成员函数,与赋值不同的是,不一定非得这样做,如果定义非成员复合赋值操作符,不会出现编译错误
  3. 改变对象状态或与给定类型紧密联系的其他一些操作符, 如自增、自减和解引用,通常就定义为类成员。 
  4. 对称的操作符,如算术操作符、相等操作符、关系操作 符最好定义为友元函数。 
  5.  >>、<<操作符定义为友元函数。

运算符 + 与 += 的重载区别与联系?

回过头来看,在重载了 + 运算符时,通常会同时重载 += 运算符,思考下这两个运算符应该怎么重载? 

  1. operator+  通常定义成非成员,operator+=通常定义成成员 
  2. operator+= 返回操作数的引用,而operator+返回一个临时对象
  3. 通常我们用 operator += 来实现 operator+
  4. 其它算术操作符(+,-,*,/,%)同+ 

废话少说,上代码解释:

//重载 +=(复数相加)
	Complex operator +=(const Complex &c2)
	
		real+=c2.real;
		image+=c2.image;
		return *this;
	

//重载 +(复数相加)
	friend Complex operator +(const Complex &c1,const Complex &c2)
	
		Complex c(c1);
		c+=c2;
		return c;
	

为什么要用 += 来实现 + 呢?

基于效率考虑,这样可以使 += 返回一个操作数的引用,而 + 不行。(故 += 效率远大于 + )


最后几点:

如果一个运算符想用某个基本类型的数据调用,它就必须定义成全局的友元函数。

如: complex a; complex b = 2 + a;   

这个 operator+ 不能是 complex 类的成员函数,也不能是其他类的成员函数,因为不存在一个函数: 

     complex & int.operator+ ( complex& rhs) 


最后的最后,贴出一些简单运算符的重载代码,其他的自行思考。

//复数相关运算的运算符重载


#include <iostream>
using namespace std;

class Complex

private:
	double real;
	double image;
public:

	Complex()real=0;image=0;
	Complex(double r,double i)
	
	real=r;
	image=i;
	
	
	void ShowComplex()
			
		cout<<real;
		if(real>0) cout<<"+";
		cout<<image<<"i"<<"\\n";
	
	
	//重载 <<
	friend ostream& operator <<(ostream &out,const Complex &c)
	
		out<<c.real;
		if(c.real>0) out<<"+";
		out<<c.image<<"i"<<"\\n";
		return out;
	
	
	//重载 >>
	friend istream& operator >>(istream &in,Complex &c)
	
		cout<<"input r:"<<endl;
		in>>c.real;
		cout<<"input i:"<<endl;
		in>>c.image;
		return in;
	
	
	//重载 =
	Complex operator =(const Complex &c)
	
		if(this == &c) return *this; 	//重要!防止自赋值导致低效
		real=c.real;
		image=c.image;
		return *this;
	

	//重载 -=
	Complex operator -=(const Complex &c2)
	
		real-=c2.real;
		image-=c2.image;
		return *this;
	

	//重载 +=(两个复数相加)
	Complex operator +=(const Complex &c2)
	
		real+=c2.real;
		image+=c2.image;
		return *this;
	

	//重载 +=(复数与实数相加)
	Complex operator +=(double c2)
	
		real+=c2;
		return *this;
	

	//重载 *=
	Complex operator *=(const Complex &c2)
	
		real=real*c2.real-image*c2.image;
		image=image*c2.real+real*c2.image;
		return *this;
	

	//重载/=
	Complex operator /=(const Complex &c2)
	
		real=(real*c2.real+image*c2.image)/(c2.real*c2.real+c2.image*c2.image);
		image=(image*c2.real-real*c2.image)/(c2.real*c2.real+c2.image*c2.image);
		return *this;
	


	//重载 +(复数相加)
	friend Complex operator +(const Complex &c1,const Complex &c2)
	
		Complex c(c1);
		c+=c2;
		return c;
	

	//重载 +(复数与实数相加)
	friend Complex operator +(const Complex &c1,double c2)
	
		Complex c(c2,0);
		c+=c1;
		return c;
	

	//重载 -
	friend Complex operator -(const Complex &c1,const Complex &c2)
	
		Complex c(c1);
		c-=c2;
		return c;
	

	//重载 *
	friend Complex operator *(const Complex &c1,const Complex &c2)
	
		Complex c(c1);
		c*=c2;
		return c;
	

	//重载 /
	friend Complex operator /(const Complex &c1,const Complex &c2)
	
		Complex c(c1);
		c/=c2;
		return c;
	
	
;

int main()

	Complex c;
	Complex c1(1.5,2.5);
	Complex c2(2.5,3.5);
	cout<<"c1:\\t";
	c1.ShowComplex();
	cout<<"c2:\\t";
	c2.ShowComplex();
	c=c1+c2;
	cout<<"c1+c2:\\t";
	c.ShowComplex();
	cout<<"c1+5:\\t";
	c1+=5;
	c1.ShowComplex();
	c=c1-c2;
	cout<<"c1-c2:\\t";
	c.ShowComplex();
	c=c1*c2;
	cout<<"c1*c2:\\t";
	c.ShowComplex();
	c=c1/c2;
	cout<<"c1/c2:\\t";
	c.ShowComplex();
	cin>>c1;
	cout<<"c:"<<c1;
	return 0;


运算符重载表:

可以重载C++中除下列运算符之外的所有运算符:

. .* :: ?: sizeof typeid # static_cast<> dynamic_cast<>   const_cast<>   reinterpret_cast<>     ||   &&   ,

可重载运算符: 

+ - * / % ^ & | ~ ! = < > <= >= ++ -- >> == != += -= /= %= ^= &= |= *= <<= >>= [] () -> ->* new new [] delete delete [] 






以上是关于C++复数运算的运算符重载的主要内容,如果未能解决你的问题,请参考以下文章

C++重载双目运算符

复数类的运算

关于双目运算符的重载问题

YTU 2439: C++习题 复数类--重载运算符+

C++复数运算的运算符重载

YTU 2441: C++习题 复数类--重载运算符2+