C++笔记之运算符重载详解

本文主要内容:

    • 一.运算符重载简介
        • 1.运算符重载实质
        • 2.规则和限制
    • 二.运算符的重载形式
        • 1.重载为类成员函数
        • 2.重载为友元函数
        • 3.特殊介绍

一.运算符重载简介

1.运算符重载实质

(1) 对已有的运算符赋予多重含义
(2) 必要性:C++预定义的运算符运算对象只能是基本数据类型,而不适用于用户自定义类型(如 类)
(3) 实现机制:

  • 将指定的运算符表达式转化为对运算符函数的调用,运算对象转化运算符函数的实参
  • 编译系统对重载运算符的选择,遵循函数重载的选择原则。

2.规则和限制

(1) 可以重载的运算符:
C++笔记之运算符重载详解_第1张图片
上述运算符中,[ ] 是下标运算符,()是函数调用运算符,++和 – 是自增自减运算符包括前置后置形式。
另外,有4个运算符不能重载,即 长度运算符 sizeof、条件运算符 ?:、成员选择符== . == 和域解析运算符== : : == 。
(2) 其他规则

  • 不改变原运算符的优先级和结合性。
  • 不能改变操作数个数。
  • 经重载的运算符,其操作数中至少应该有一个是自定义类型。

二.运算符的重载形式

1.重载为类成员函数

声明形式:

函数类型 operator 运算符(形参)
{
	函数体;
}

重载为友元函数时,参数个数=原操作数个数-1 (后置++、–除外)

(1) 双目运算符
如果重载运算符B 为类成员函数,使之能实现表示式oprd1 B oprd2, 其中oprd1位 A类对象,则 B 可被重载为 A 类的成员函数,形参类型是 oprd2 所属类型。经重载过,表达式 oprd1 B oprd2 相当于oprd1.operator B(oprd2)

举例说明:
重载 + 、+=,实现复数类complex 对象想相加。

#include    
using namespace std;
class complex
{
public:
	complex(double real = 0.0, double image = 0.0)
	{ this->real = real;this->image = image;}
	complex operator + (complex &c1); // 重载 + 为成员函数
	void operator += (complex &c1); //重载 +=
	int showComplex(); //输出复数
private:
	double real;
	double image;
};
complex complex::operator + (complex &c1) //重载 + 为成员函数的实现
{
  /*complex c;						//实现1:创建一个局部变量
	c.real = c1.real + real;
	c.image = c1.image + image;
	return c;  
	*/
	return complex(c1.real + real, c1.image + image); //实现2:创建临时变量,直接一句代码返回结果
}
void complex::operator+=(complex &c1)  //重载 += 
{
	real += c1.real;
	image += c1.image;
}
int complex::showComplex()
{
	cout << real;
	if (image > 0)
		cout << "+";
	if (image != 0)
		cout << image << "i" << endl;
	return 0;
}
int main()
{
	complex c1(1, 2), c2(-3, 4), c3;
	cout << "c1=";c1.showComplex();
	cout << "c2=";c2.showComplex();
	c3 = c1 + c2; //重载+ 的使用,两边都为complex对象
	cout << "c3=c1+c2=";c3.showComplex(); 
	c2 += c1;//重载+= 的使用,两边都为complex对象
	cout << "c2+=c1结果: ";c2.showComplex(); 
	return 0;
}

(2) 前置单目运算符
如果要重载 U 为类成员函数,使之能够实现表达式 U oprd,其中 oprd 为A类对象,则 U 可被重载为 A 类的成员函数,无形参。经重载后,表达式 U oprd 相当于 oprd.operator U()
(3) 后置单目运算符 ++和–
复习一下++ 、–

示例 解释
++i i自增1后再参与运算;i的值加1,++i的值也加1
i++ i参与运算后,i的值再加1;i的值加1,i++的值不变

如果要重载 ++或–为类成员函数,使之能够实现表达式 oprd++ 或 oprd-- ,其中 oprd 为A类对象,则 ++或-- 可被重载为 A 类的成员函数,且具有一个 int 类型形参。经重载后,表达式 oprd++ 相当于oprd.operator ++(0)

举例说明(1):
++ 实现Point类的坐标自增

#include    
using namespace std;
class Point
{
private:
	int x, y;
public:
	Point(int x, int y)
	{
		this->x = x;
		this->y = y;
	}
	Point() {}
	Point & operator++();//重载前置单目运算符++为成员函数    ++i i加1,i++整体值加1
	Point operator++(int);//重载后置单目运算符++成员函数     i++ 只有i加1,i++值不变
	void showPoint();
};
Point & Point::operator++ () //++前置 原来的点变,x,y变
{
	++x;
	++y;
	return *this;
}
Point Point::operator++(int) //后置++  原来的点不变,x,y加1
{
	Point old = (*this); //将旧对象放在新对象里,末尾返回旧对象
	x++;
	y++;
	return old;
}
void Point::showPoint() {
	cout << "(" << x << "," << y << ")" << endl;
}

int main() {
	Point p1(1, 2), p2(3, 4), p3, p4;
	p3 = ++p1; //++前置
	p3.showPoint();  //(2,3)
	p4 = p2++; //后置++
	p4.showPoint(); //(3,4)
	return 0;
}

举例说明(2)
++ 实现时间的自增

#include    
using namespace std;

class Clock {
private:
	int Hour, Minute, Second;
public:
	Clock(int NewH = 0, int NewM = 0, int NewS = 0);
	void ShowTime();
	Clock operator ++();// 前置单目运算符重载
	Clock operator++(int);//后置单目运算符重载
};
Clock::Clock(int NewH, int NewM, int NewS) {
	Hour = NewH;
	Minute = NewM;
	Second = NewS;
}
Clock Clock::operator++() // 前置单目运算符重载
{
	Second++;
	if (Second >= 60)
	{
		Second = Second - 60;
		Minute++;
		if (Minute >= 60)
		{
			Minute = Minute - 60;
			Hour++;
			Hour = Hour % 24;
		}
	}
	cout << "++Clock:";
	return *this;
}
Clock Clock::operator++(int)  // 后置单目运算符重载
{
	Clock old = *this;
	Second++;
	if (Second >= 60)
	{
		Second = Second - 60;
		Minute++;
		if (Minute >= 60)
		{
			Minute = Minute - 60;
			Hour++;
			Hour = Hour % 24;
		}
	}
	cout << "Clock++:";
	return old;
}
void Clock::ShowTime() 
{
	cout << Hour << ":" << Minute << ":" << Second << endl;
}

int main() {
	Clock myclock(23, 59, 59);
	cout << "First clock:";
	myclock.ShowTime();
	(++myclock).ShowTime();
	(myclock++).ShowTime();
	return 0;
}

2.重载为友元函数

声明形式:

friend 函数类型  operator 运算符(形参)
{
       函数体;
}

重载为友元函数时 , 参数个数=原操作数个数,且至少应该有一个自定义类型的形参。

(1) 运算符友元函数的设计

  • 如果需要重载一个运算符,使之能够用于操作某类对象的私有成员,可以此将运算符重载为该类的友元函数。
  • 函数的形参 代表 依自左至右次序排列的各操作数。
  • 后置单目运算符 ++和–的重载函数形参列表中要增加一个int,但不必写形参名

双目运算符 B 重载后,表达式:oprd1 B oprd2 等同于:operator B(oprd1,oprd2)
前置单目运算符 B重载后,表达式:B oprd 等同于:operator B(oprd)
后置单目运算符 ++和–重载后,表达式:oprd B 等同于:operator B(oprd,0)

举例说明(1):
+、-、=、+=重载为友元函数 ,以complex类举例

#include
using namespace std;
class complex	//复数类声明
{
public:	     
	complex(double real = 0.0, double image = 0.0) { this->real = real; this->image = image; }	//构造函数
	friend complex operator + (complex c1, complex c2); //运算符+ 重载为友元函数
	friend complex operator - (complex c1, complex c2); //运算符- 重载为友元函数
	friend bool operator==(const complex &c1, const complex &c2);
	friend complex operator += (complex &c1,complex &c2); //运算符+= 重载为友元函数
	void showComplex();	//显示复数的值
private:	
	double real;
	double image;
};
complex operator+(complex c1, complex c2)
{
	return complex(c2.real + c1.real, c2.image + c1.image);
}
complex operator-(complex c1, complex c2)
{
	return complex(c1.real - c2.real, c1.image - c2.image);
}
bool operator==(const complex &c1, const complex &c2) {
    if (c1.real == c2.real && c1.image == c2.image) {
        return true;
    } else {
        return false;
    }
}
complex operator+=(complex &c1, complex &c2)
{
	c1.real += c2.real;
	c1.image += c2.image;
}
void complex::showComplex()
{
	cout << real;
	if (image > 0)
		cout << "+";
	if (image != 0)
		cout << image << "i" << endl;
}
int main()
{
	complex c1(1, 2), c2(-3, 4), c3, c4;
	cout << "c1=";c1.showComplex();
	cout << "c2=";c2.showComplex();
	c3 = c1 + c2;
	cout << "c3=c1+c2=";c3.showComplex();
	c4 = c1 - c3;
	cout << "c4=c1-c3=";c4.showComplex();
	if(c4==c1){
		cout << "c4==c1"<<endl;
	}
	return 0;
}

举例说明(2):
c1+c2, c1+i, i+c1

#include 
using namespace std;
class complex
{
public:
	complex(double real = 0.0, double image = 0.0)
	{this->real = real;this->image = image;}
	void show();
	friend complex operator + (complex&c1,  complex&c2);
	friend complex operator + (complex&c1, int c2);
	friend complex operator + (int c1, complex&c2);
private:
	double real;
	double image;
};
complex operator + (complex&c1, complex&c2)
{
	complex c;
	c.real = c1.real + c2.real;
	c.image = c1.image + c2.image;
	return c;
}
complex operator + (complex&c1, int c2)
{
	complex c;
	c.real = c1.real + c2;
	c.image = c1.image;
	return c;
}
complex operator + (int c1, complex&c2)
{
	complex c;
	c.real = c2.real + c1;
	c.image = c2.image;
	return c;
}
void complex::show()
{
	cout << "(" << real << " + " << image << "i" << ")"<<endl;
}
int main()
{
	complex c1(1, 2),c2(3,4),c3,c4,c5;
	int i = 2;
	cout << "i=" << i << endl;
	cout << "c1=";c1.show();
	cout << "c2=";c2.show();
	c3 = c1 + c2;
	cout << "c3=c1+c2=";c3.show();
	c4 = c1+i;
	cout << "c4=c1+i=";c4.show();
	c5 = i + c1;
	cout << "c5=i+c1=";c5.show();
	return 0;
}

3.特殊介绍

(1) 下标运算符[ ] 重载为成员函数
该重载函数在类中的声明格式:

返回值类型 & operator[](参数);
const 返回值类型 & operator[](参数) const;

使用第一种声明方式,[ ]不仅可以访问元素,还可以修改元素;
使用第二种声明方式,[ ]只能访问而不能修改元素;

在实际开发中,我们应该同时提供以上两种形式,这样做是为了适应 const 对象,因为通过 const 对象只能调用 const 成员函数,如果不提供第二种形式,那么将无法访问 const 对象的任何元素;

#include 
#include 
using namespace std;

class MyString {
public:
	MyString();//默认构造函数
	MyString(const char*const); //构造函数
	MyString(const MyString&);//复制构造函数
	~MyString();//析构函数
	char & operator[](int n); //成员函数重载[],该形式可修改可访问元素
	char operator[](int n)const;//常成员函数重载[],用于const对象调用
	int getLen()const {	return length;}
	const char *getMyString()const {return myString;}
private:
	char *myString;
	int length;
};
MyString::MyString() { //默认构造函数
	myString = new char[1];
	myString[0] = '\0';
	length = 0;
}
MyString::MyString(const char *const str) { //构造函数
	length = strlen(str);
	myString = new char[length + 1];
	for (int i = 0;i < length;i++)
		myString[i] = str[i];
	myString[length] = '\0';
}
MyString::MyString(const MyString &str) { //复制构造函数
	length = str.getLen();
	myString = new char[length + 1];
	for (int i = 0;i < length;i++)
		myString[i] = str[i];
	myString[length] = '\0';
}
MyString::~MyString() { //析构函数
	delete[]myString;
	length = 0;
}
 
char & MyString::operator [](int n) { //下标运算符[]重载
	if (n > length | n < 0)
		cout << "数组下标越界!" << endl;
	else
		return myString[n];
}
char MyString::operator [](int n)const
{
	if (n > length | n < 0)
		cout << "数组下标越界!" << endl;
	else
		return myString[n];
}
int main() {
	MyString str1("xiaobencong  zhen shuai!");
	cout << "str1:     " << str1.getMyString() << endl;
	cout << "str1[4]:  " << str1[4] << endl;
	cout << "str1[-1]: " << str1[-1] << endl;
	cout << "str1[99]:" << str1[999] << endl;
	return 0;
}

(2) 关于 什么时候用友元函数和成员函数,大家可以阅读这篇文章
C++ 运算符重载

欢迎关注微信公众号:学编程的金融客,作者:小笨聪

你可能感兴趣的:(C++)