运算符重载

运算符重载

学习运算符重载,让运算符能做一些原来做不了的事情,方便它的使用

一、运算符重载的概念

1、什么是运算符重载

1.重载:重新载入,就像之前学的函数重载,对一个已有的函数赋值一个新的定义,因此同一个函数名就可以有不同的含义。

2.运算符也是可以重载的,比如cout在输出一个变量的时候,能接受不同类型的数据并输出,他就是重载了<<运算符,这个就是运算符重载

3.所以运算符重载指的是对已有的运算符重新定义新的运算规则,以适应不同的数据类型,当然重载之后之前的运算规则还是有的

2、为什么要进行运算符重载

1.运算符重载之后可以让运算符去适应不同的数据类型,对于基本数据类型,系统给出了运算符的操作规则,对于自定义数据类型来说,系统不知道该给出什么规则

class student 
{
    int id;
    int age;
    char name[20];
    public:
    student(int id,int age,const char* name)
    {
        this->id=id;
        this->age=age;
        strcpy(this->name,name);
    }
}
student stu1(1,23,"张三");
student stu2(2,24,"李四");
stu1+stu2;//如果是这样相加,那么应该加的是什么呢?编译器是不知道的,所以编译器就提供了运算符重载这个机制,让用户自定义运算符的运算规则

二、运算符重载

1、运算符重载类中定义

1.关键字:operator,通过关键字来定义运算符重载(跟写个函数一样)

2.定义:

函数返回值类型 operator 要加载的运算符(参数列表)

{

​ 函数体;

}

这里把运算符的使用,理解为调用函数,只是和平时的调用函数有一点区别

#include<iostream>
#include<string>
using namespace std;
class student
{
	int id;
	int age;
	string name;
public:
	student(int age)
	{
		this->age = age;
		id = 1;
		name = "sss ";
	}
	student(int id, int age, string name)
	{
		this->id = id;
		this->age = age;
		this->name = name;
	}
	void showstudent()
	{
		cout << id << "\t" << age << "\t" << name << endl;
	}
	student operator+(student& p1)//这个函数会返回一个新的对象
	{
		int x=this->age + p1.age;
		student p(x);
		return p;//返回的是一个对象,会调用拷贝构造
	}
	int operator-(int x)
	{
		return this->id - x;
	}
	void operator+(student&p2)
	{
		cout << this->id + p2.id << endl;

	}
};
//1.操作这个运算符之后,返回值类型是什么
int main()
{
	student p1(0, 1, "yunfei");
	int x = p1.operator-(1);
	cout << x << endl;

	student stu1(1, 23, "张三");
	student stu2(2, 24, "李四");
	//student stu3 = stu1.operator+(stu2);
	//student stu3 = stu1 + stu2;
	stu1 + stu2;
	//stu3.showstudent();

	


	system("pause");
	return 0;
}

注意:因为我们这个运算符是在类中写的,所以是通过对象调用的,那么this指针会占一个参数,而且是第一个参数,也就是说我们重载一个运算符,是在类中,而这个运算符是个单目运算符,那么参数列表就不用写东西了,是双目运算符,那么就需要传另一个参数进来

绝大部分的运算符重载都可以参照上面这个+号重载

2、运算符重载的特点

1.几乎所有的运算符都可以被重载,除了 . :: ()?()😦) sizeof()

2.运算符重载基本出现在类中和结构体中

3.运算符可以理解为函数的一个表现

3、运算符重载的注意事项

1.重载运算符,这个重载的运算符还是满足原来的原则,但不能说重载+号,结果做的事-号的事,这样会使运算符的运用上增加很大的难度

2.运算符重载的参数,类中重载调用对象会占一个参数,就是this会占一个参数,参数列表就是用来表示运算符的操作的

3.对于运算符重载的调用,可以直接使用运算符,也可以通过对象 . 出来调用

4.考虑返回值,不同的运算符有不同的返回值,要记得满足运算符原来的规则

4、使用友元函数,实现运算符重载

1.类在已经实现且部分修改的情况下下,需要进行运算符重载,就可以通过友元的方式来进行重载

#include<iostream>
#include<string>
using namespace std;
class person
{
	int id;
	int age;
	string name;
public:
	person(int id, int age, string name)
	{
		this->id = id;
		this->age = age;
		this->name = name;
	}
	void showperson()
	{
		cout << id << "\t" << age << "\t" << name << endl;
	}
	friend int operator+(person&p1, person&p2);

};
//形参使用的是类对象的引用,在实参传对象的时候不会调用拷贝构造
int operator+(person&p1, person&p2)
{
	return p1.id + p2.id;
}

//1.操作这个运算符之后,返回值类型是什么
int main()
{
	person stu1(1, 23, "张三");
	person stu2(2, 24, "李四");
	int x = operator+(stu1, stu2);//显示调用
	int y = stu1 + stu2;//隐式调用
	cout << x << endl << y << endl;
	system("pause");
	return 0;
}

容器:

#include<iostream>
#include<vector>
using namespace std;

int main()
{
	//int 是v1这个容器存的类型
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		//push_back()是一个函数,功能是尾插元素
		v1.push_back(i + 1);
	}
	for (int i = 0; i < 10; i++)
	{
		cout << v1[i] << "\t";
	}
	
	system("pause");
	return 0;
}

左移右移运算符重载:

#include<iostream>
using namespace std;
class person
{
	int id;
public:
	person(int id)
	{
		this->id = id;
	}
	friend ostream& operator<<(ostream& os, person& p1);
	friend istream & operator>>(istream & in, person & p2);
};
//左移右移运算符重载,必须在类外重载,通过友元实现
ostream& operator<<(ostream& os, person& p1)//左移运算符
{
	os << p1.id << endl;
	return os;//返回的是一个cout,而且只能用引用
}
istream & operator>>(istream & in, person & p2)//右移运算符
{
	in >> p2.id;
	return in;
}

int main()
{
	person p1(10), p2(20);
	cin >> p1 >> p2;
	cout << p1 << endl << p2 << endl;
	
	
	system("pause");
	return 0;
}

前++,后++运算符重载:

#include<iostream>
using namespace std;
class person
{
	int id;
public:
	person(int id)
	{
		this->id = id;
	}
	person& operator++()//前++
	{
		this->id++;
		return *this;
	}
	person& operator++(int)//后++,int是一个占位符,用来区分前++和后++的
	{
		static person temp = *this;//引用不能返回局部变量,要用静态变量
		this->id++;
		return temp;
	}
	friend ostream& operator<<(ostream& os, person& p1);
	friend istream & operator>>(istream & in, person & p2);
};

//左移右移运算符重载,必须在类外重载,通过友元实现
ostream& operator<<(ostream& os, person& p1)//左移运算符
{
	os << p1.id << endl;
	return os;//返回的是一个cout,而且只能用引用
}
istream & operator>>(istream & in, person & p2)//右移运算符
{
	in >> p2.id;
	return in;
}

int main()
{
	person p1(10), p2(20);
	//cin >> p1 >> p2;
	//cout << p1 << endl << p2 << endl;
	cout << p1 ;//10
	cout << p1++ ;//10
	cout << p1 ;//11
	cout << ++p1 ;//12
	cout << p1 ;//12

	
	system("pause");
	return 0;
}

等号运算符重载:

#include<iostream>
using namespace std;
class person
{
	char* name;
public:
	person(const char* name)
	{
		this->name = new char[strlen(name) + 1];
		strcpy(this->name, name);
	}
	person& operator=(person&p1)//用不用引用传参,要看返回的对象会不会消失
	{
		if (this->name != NULL)
		{
			delete[]this->name;
			this->name = NULL;
		}
		this->name = new char[strlen(p1.name) + 1];
		strcpy(this->name, p1.name);
		return *this;
	}
	void show()
	{
		cout << name << endl;
	}
	~person()//如果有申请函数,就要加上析构函数
	{
		if (name != NULL)
		{
			delete[]name;
			name = NULL;
		}
	}
};

int main()
{
	{
		person p1("张三"), p2("李四"), p3("王五");
		p1 = p2 = p3;
		p1.show();
		p2.show();
		p3.show();
	}//加上大括号,让对象死亡,就能调用析构函数
	system("pause");
	return 0;
}

智能指针和==号运算符重载:

#include<iostream>
using namespace std;

class person
{
	int id;
public:
	person(int id)
	{
		this->id = id;
	}
	void show()
	{
		cout << id << endl;
	}
	bool operator==(person& p)
	{
		return this->id == p.id;
	}
	~person()
	{
		cout << "person的析构函数" << endl;
	}
};
class smartpointer
{
	person* ps;//包含你要new出来的对象的类的指针
public:
	smartpointer(person* p)
	{
		ps = p;
	}
	//重载->
	person* operator->()//传回来的是地址,不是对象,不用引用
	{
		return ps;
	}
	//重载*
	person& operator*()//返回的是对象,会调用拷贝构造,所以用返回值用引用,就不会再调用拷贝构造了
	{
		return *ps;//得到一个对象,
	}
	~smartpointer()
	{
		if (ps != NULL)
		{
			delete ps;
			ps = NULL;
		}
	}

};


int main()
{
	{
		smartpointer p(new person(5));
		p->show();
		(*p).show();
		person p1(1), p2(3);
		cout << (p1 == p2) << endl;
	}//有三个对象,所以析构函数执行了三次
	system("pause");
	return 0;
}

[]运算符重载:

#include<iostream>
using namespace std;
class person
{
	char* name;
public:
	person(const char* name)
	{
		this->name = new char[strlen(name) + 1];
		strcpy(this->name, name);
	}
	char& operator[](int index)
	{
		return name[index];
	}
	~person()
	{
		if (name != NULL)
		{
			delete[]name;
			name = NULL;
		}
		cout << "这是析构函数" << endl;
	}
};
int main()
{
	person p("asdfg");
	cout << p[3] << endl;
	system("pause");
	return 0;
}

c++引用作为函数返回值:

1.以引用返回函数值,定义函数时需要在函数名前加 &

2.用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本

3.返回值为引用的时候,返回的是一个地址,隐形指针

4.当返回值不是引用时,编译器会专门给返回值分配出一块内存的

引用作为返回值,必须遵守以下规则:

(1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。

(2)不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。

(3)可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常 量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

posted @ 2021-02-22 22:36  kisfly  阅读(140)  评论(0编辑  收藏  举报