运算符重载

运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据时做出不同的行为。

例如:学生类包括姓名,年龄,班级,成绩等,定义了两个学生变量,想比较它们的大 小,如何比较大小呢?

为“学生”类重载“>”符号,然后在这个方法里实现比较的意义:按年龄、按成绩或按姓名首字母比较等。

运算符重载的语法格式

重载的运算符是具有特殊名字的函数:它们的名字由关键字operator,后跟要重载的运算符。

简单例子

“+”运算符的重载

#include <iostream>
using namespace std;
 
class A
{
private:
	int x, y;
public:
	A(int x1 = 0, int y1 = 0)
	{
		x = x1;
		y = y1;
	}
	A operator+(const A& a)const//重载-运算符的实现
	{
		return A(x + a.x, y + a.y);//调用A类构造函数创建一个临时匿名对象作为函数返								回
	}
	void show()
	{
		cout << "x=" << x << "," << "y=" << y << endl;
	}
};
int main()
{
	A a1(1, 2);
	A a2(3, 4);
	A a;
	a = a1 + a2;
	a.show();
}

输出结果

const一般用在代码前面表示常量,只可读不可改,这里用在函数中表示常成员函数: 只读函数,只可读不可改数据成员的值。也就是时候const定义的函数里面的数据成员 的值不可改变

代码分析

在A类中重载了运算符+,该重载只对A类对象有效。

执行a=a1+a2;语句时,编译器检测到+号左边是一个A类对象(+号具有左结合性,所以 先检测左边),就会调用成员函数operator+(),也就是转换为下面的形式:

a=a1.operator+(a2);    //a1是要调用函数的对象,a2是函数的实参

可见:

重载运算符并没有改变其原来的功能,只是增加了对自定义数据类型的运算功能。

运算符重载的两种方式

1,重载为类的成员函数

a.双目运算符

如果是双目运算符重载为类的成员函数,则它有两个操作数:左操作数是对象本身的数据,由this指针指出,右操作数则通过运算符重载函数的参数表来传递。

调用格式为:

左操作数.运算符重载函数(右操作数)

#include <iostream>
using namespace std;
 
class A
{
private:
	int x, y;
public:
	A(int x1 = 0, int y1 = 0)
	{
		x = x1;
		y = y1;
	}
	A operator+(const A& a)const
	{
		A t;
		t.x = this->x + a.x;
		t.y = this->y + a.y;
		return t;
	}
	void show()
	{
		cout << "x=" << x << "," << "y=" << y << endl;
	}
};
int main()
{
	A a1(1, 2);
	A a2(3, 4);
	A a;
	a = a1 + a2;
	a.show();
}
b.单目运算符

如果是单目运算符重载为类的成员函数,则要分为前置(++i)和后置(i++)运算符。

如果是前置运算符,则它的操作数是函数调用者,函数没有参数。

调用格式为:

操作数.运算符重载函数()

#include <iostream>
using namespace std;
 
class A
{
private:
	int x, y;
public:
	A(int x1 = 0, int y1 = 0)
	{
		x = x1;
		y = y1;
	}
	A operator++()//++i 前置++实现
	{
		++x;	//先自增
		++y;
		return *this;	//后引用
	}
	A operator++(int)	//i++ 后置++实现
	{
		//int参数没有任何意义,只是为了区分是前置还是后置形式
		A a = *this;	//保存对象引用
		++(*this);	//自增,调用前面实现的前置++
		return a;	//返回先前保存的对象
	}
	void show()
	{
		cout << "x=" << x << "," << "y=" << y << endl;
	}
};
int main()
{
	A a1(1, 2), a2(3, 4);
	(a1++).show();
	(++a2).show();
	return 0;
}

输出结果

实现前置“++”时,数据成员进行自增运算,然后返回当前对象(即this指针所指向的对象)。实现后置“++”时,创建了一个临时对象来保存当前对象的值,然后再将当前对象自增,最后返回的是保存了初始值的临时对象。

注意:前置单目运算符和后置单目运算符的最主要区别是函数的形参,后置单目运 算符带一个int型形参,但它只起区分作用。

2,重载为类的友元函数

运算符重载为类的友元函数,只是在函数前加一个friend关键字。

说明:运算符重载为类的友元函数时,由于没有隐含的this指针,因此,操作数的个数没有变化,所有的操作数都必须通过函数形参进行传递,函数的参数与操作数 自左自右保持一一对应。

(2)调用格式

operator 运算符(参数1,参数2);

例如:调用a1+a2相当于函数调用operator+(a1, a2)

下面看一个例子:

将”+”运算符重载为类的友元函数


#include <iostream>
using namespace std;
 
class A
{
private:
	int x, y;
public:
	A(int x1 = 0, int y1 = 0)
	{
		x = x1;
		y = y1;
	}
	friend A operator+(const A& a, const A& b);
	void show()
	{
		cout << "x=" << x << "," << "y=" << y << endl;
	}
};
A operator+(const A& a, const A& b)
{
	return A(a.x + b.x, a.y + b.y);
}
int main()
{
	A a1(1, 2), a2(3, 4);
	A c;
	c = a1 + a2;
	c.show();
	return 0;
}

两种重载方式的选择

常用运算符的重载

1,输入输出运算符的重载

C++标准库对左移运算符<<和右移运算符>>分别进行了重载,使其能够用于不同数 据的输入输出。对于基本类型数据(如bool、int、double等)和标准库所包含的 类(如string、complex、ofstream、ifstream等)可以直接使用输入运算符“>>”、 输出运算符“<<”进行读写操作。对于自己定义的新数据类型(如:类对象)则需要重载这两个运算符。

由于输入输出操作的第一个操作数为ostream/istream对象,也就是说左侧的运算对象必须是ostream/istream对象,如果重载成类的成员函数,则左侧的操作对象将是我们定义的一个类对象(需要修改标准库中的类,显然不是我们所期望的)。因此输入输出运算符不可重载为类的成员函数,只能重载为非成员函数 (全局函数,友元函数)。

说明
(1)对于输入运算符来说,第一个参数是istream对象的引用,第二个参数是要 向其中存入数据的对象,不能为常量。返回istream类对象的引用(可作为下次调用时的第一个参数),是为了能够连续读取:cin>>c1>>c2;,让代码书写更加漂亮。 如果不返回引用,就只能一个一个地读取:cin>>c1;  cin>>c2;

cin>> c; 可以理解为:operator >> (cin , c);

(2)对于输出运算符“<<”来说,第一个参数是ostream对象引用,因为向流中写入数据会改变流的状态 ,所以不能用const修饰ostream对象。由于采用了引用 的方式进行参数传递,并且也返回了对象的引用,所以重载后的运算符可以实现连 续输出。

cout<< c; 可以理解为:operator<< (cout , c);

#include <iostream>
using namespace std;
 
class A
{
private:
	int x, y;
public:
	A(int x1 = 0, int y1 = 0)
	{
		x = x1;
		y = y1;
	}
	friend istream& operator>>(istream& is, A& a);
	friend ostream& operator<<(ostream& os, const A& a);
};
istream& operator>>(istream& is, A& a)
{
	is >> a.x >> a.y;
	return is;
}
ostream& operator<<(ostream& os, const A& a)
{
	os << "x=" << a.x << "," << "y="<<a.y ;
	return os;
}
int main()
{
	A a1(1, 2);
	cin >> a1;
	cout << a1;
	return 0;
}

重载输入输出运算符后,便可像基本数据类型一样直接对类对象进行输入输出,不用再编写用于输入输出对象数据成员的show()等成员函数,而且使用起来更为简洁。

关系运算符的重载

关系运算符(如“==”或“<”)也是较常用的一类运算符,同样,若类对象间需要 完成比较操作,也应完成对关系运算符的重载。关系运算符重载的过程非常直观。 重载关系运算符一般都返回true或false值。

关系运算符共有六个,并且相互之间有对应关系,如小于运算符“<”与大于运算 符“>”对应,因此对于关系运算符的重载应成对完成。

可重载为类的成员函数也可重载为类的友元函数(具有对称性的运算符通常重载为 类的友元函数)


#include <iostream>
using namespace std;
 
class A
{
private:
	int x, y;
public:
	A(int x1 = 0, int y1 = 0)
	{
		x = x1;
		y = y1;
	}
	friend bool operator>(const A& a1,const A& a2);
	friend bool operator<(const A& a1,const A& a2);
	friend bool operator==(const A& a1, const A& a2);
};
bool operator>(const A& a1, const A& a2)
{
	return a1.x > a2.x;
}
bool operator<(const A& a1, const A& a2)
{
	return a1.x < a2.x;
}
bool operator==(const A& a1, const A& a2)
{
	return a1.y == a2.y;
}
int main()
{
	A a1(1, 2);
	A a2(3, 2);
	if (a1 > a2)
	{
		cout << "a1的x大于a2的x"<<endl;
	}
	else if (a1 < a2)
	{
		cout << "a1的x小于a2的x" << endl;
	}
	if (a1 == a2)
	{
		cout << "a1的y都等于a2的y" << endl;
	}
	return 0;
}

赋值运算符的重载

若类中没有显式定义赋值运算符的重载函数,编译器将自动提供一个默认的重载函数,完成数据成员的“浅拷贝”(跟默认拷贝构造函数一样,就是把一个对象的数据成员的值复制给另一个对象对应的数据成员)。若类中的数据成员上需要附加额 外的信息,比如类中存在指针成员(编译器默认的赋值运算符不能满足要求,会出现内存泄露),指针成员指向新开辟的空间,此时需要重载赋值运算符,以实现“深拷贝”。


注意:赋值运算符只能被重载为类的成员函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值