运算符重载
运算符重载
学习运算符重载,让运算符能做一些原来做不了的事情,方便它的使用
一、运算符重载的概念
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)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常 量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。