4.5运算符重载
概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
operator是C++的关键字,它和运算符一起使用,表示一个运算符函数,理解时应将 operator运算符 整体上视为一个函数名。
4.5.1加号运算符重载
实现两个自定义数据类型相加的运算
- 有成员函数实现
#include<iostream>
using namespace std;
#include<string>
class goodgay{
public:
//1.通过成员函数实现重载+号
goodgay operator+(goodgay& p) opertator+是系统提供的加号标识
{
goodgay pe;
pe.m_a = m_a + p.m_a;
pe.m_b = m_b + p.m_b;
return pe;
}
int m_a;
int m_b;
};
void test01()
{
goodgay p1;
p1.m_a = 10;
p1.m_b = 10;
goodgay p2;
p2.m_a = 20;
p2.m_b = 30;
goodgay p3 = p1 + p2;
cout << "p3的m_a=" << p3.m_a << "p3的m_b=" << p3.m_b << endl;
}
int main() {
test01();
system("pause");
return 0;
}
- 全局函数实现运算符重载对于内置的数据类型的表达式的运算符是不可以修改的 比如1+1,1*2等等。
- 运算符重载的函数重载
- 不要滥用运算符重载比如在加运算符里写减的操作。
#include<iostream>
using namespace std;
#include<string>
class goodgay{
public:
//1.通过成员函数实现重载+号注意这不是有参构造
//goodgay operator+(goodgay& p) //operator+是函数名
//{
//goodgay pe;
//pe.m_a = m_a + p.m_a;
//pe.m_b = m_b + p.m_b;
//return pe;
//}
int m_a;
int m_b;
};
//运算符重载的函数重载
goodgay operator+(goodgay& p1,const int & p) //这里的int不能用引用&因为10是一个具体值常量在全局区上而形参是在栈区上的
//那个num前面没有引用是因为,下面直接赋的10,引用的本质是指针常量,所以前面不能加引用,非要加的话除非加个const
{
goodgay p3;
p3.m_a = p1.m_a + p;
p3.m_b = p1.m_b + p;
return p3;
}
//全局函数重载
goodgay operator+(goodgay& p1, goodgay& p2)//这里的goodgay是返回值类型而不是goodgay中的构造函数
{
goodgay p3;
p3.m_a = p1.m_a + p2.m_a;
p3.m_b = p1.m_b + p2.m_b;
return p3;
}
void test01()
{
goodgay p1;
p1.m_a = 10;
p1.m_b = 10;
goodgay p2;//因为有了有参构造函数系统就不再提供无参构造函数所以要自己在写一个
p2.m_a = 20;
p2.m_b = 30;
//第一种方法本质是goodgay p3 = p1.operator+(p2);
//第二种方法本质是goodgay p3 = operator+(p1,p2);
goodgay p3 = p1 + p2;
//运算符重载也可以发生函数重载
//例如goodgay p3 = p1+10;(goodgay+int)
goodgay p4 = p1 + 10;
cout << "p3的m_a=" << p3.m_a << "p3的m_b=" << p3.m_b << endl;
cout << "p3的m_a=" << p4.m_a << "p3的m_b=" << p4.m_b << endl;
}
int main() {
test01();
system("pause");
return 0;
}
4.5.2左移运算符重载
可以输出自定义数据类型==“<<”==
例如:
int a =10;
cout<<a<<endl;
person p;
p.m_A = 10;
p.m_b = 20;
cout<< p << endl;//不重载则不知道输出什么
#include<iostream>
using namespace std;
#include<string>
class person{
public://如果这里为private权限则可以将ostream&operator<<函数设为友元然后提供一个给m_a和m_b赋初值的接口函数。
//1.通过成员函数实现重载
//void operator << (person &p) //不能怎么写因为会变成p.operator<<(p)
//void operator<<(cout)//简化版本会变成p<<cout
//因此一般不会利用成员函数重载<<运算符,因为无法实现cout在左侧
int m_a;
int m_b;
};
//运算符重载的函数重载
//cout数据类型是ostream(是一个类),标准输出流对象
//cin数据类型是istream(是一个类),标准输入流对象
//只能利用全局函数重载<<
//标准输出流对象只能有一个所以要用引用
ostream& operator<<(ostream &out, person &p)///如果用void返回后续就不能使用endll因为链式函数该函数必须要返回cout才能在后面继续使用<<endl;
//引用本身是起别名因此可以把cout改成任意名称
//本质是cout<<p;
{
out << "m_A = " << p.m_a << endl;
out << "m_b = " << p.m_b << endl;
return out;
}
void test01()
{
person p;
p.m_a = 10;
p.m_b = 10;
cout << p << endl;//cout<<p就是调用了cout的运算符重载全局函数返回的是cout等于是cout<<endl;
}
int main() {
test01();
system("pause");
return 0;
}
4.5.3递增运算符重载
前置递增返回引用后置递增返回值
#include<iostream>
using namespace std;
#include<string>
class integer{
friend ostream& operator<<(ostream& cout, integer p);
public:
integer() {
num = 0;
}
//重载前置++运算符
integer& operator++() {
num++;
return *this;//*this就相当于返回对象本身
}
//后置++运算符
integer operator++(int)//这里返回用值传递因为创建的是局部对象出去后会自动销毁
//返回值不一样不能区分函数重载,只有参数不一样才能满足重载条件,因此要加一个int占位参数用于区分前置后置只能写int
{
//
integer tmp = *this;//创建临时对象同时发生了拷贝构造函数把p的属性拷贝给了tmp。
num++;
return tmp;
}
private:
int num;
};
ostream& operator<<(ostream& cout, integer p)//这是因为后置运算符重载时,不能返回局部变量的引用,只能返回的局部变量temp的拷贝(临时变量),是一个值 因此这里左移运算符重载时,
//传入形参的是temp的拷贝。不能用常数值来初始化引用型的变量
//这里传入的p是tmp是一个局部变量会被销毁因此不能用引用
{
cout << p.num;
return cout;
}
void test01()
{
integer p;
//cout << ++p << endl;
cout << p++ << endl;//这里是不可以链式递加的
cout << p << endl;
}
int main() {
test01();
system("pause");
return 0;
}
4.5.4赋值运算符重载
c++编译器至少给一个类添加四个函数
- 默认构造函数
- 默认析构函数
- 默认拷贝构造函数
- 赋值运算符operator=对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝的问题
浅拷贝就是拷贝,深拷贝就是先创建一个在堆区开辟一个内存传回地址进行拷贝
#include<iostream>
using namespace std;
class person {
public:
person(int age)
{
this->age = new int(age);
}
person& operator=(person &p)
{
if (age != NULL)
{
delete age;
age = NULL;
}
age = new int(*p.age);//开辟一个新内存空间放数据
return *this;
}
~person()
{
if (age != NULL)
{
delete age;
age = NULL;
}
return;
}
int *age;
};
void test01()
{
person p1(18);
person p2(20);
person p3(30);
p3 = p2 = p1;//浅拷贝会带来重复释放内存的错误
cout << "p2的年龄" << *p2.age << endl;
cout << "p3的年龄" << *p3.age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
这里传的是引用是不是为了防止调用拷贝构造函数产生浅拷贝,因为值传递会调用系统默认的拷贝构造函数
4.5.5关系运算符重载
重载在关系运算符可以让两个自定义类型对象进行对比操作
#include<iostream>
using namespace std;
#include<string>
class person
{
public:
person(string name,int age) {
this->name = name;
this->age = age;
}
bool operator==(person &p)
{
if (this->name == p.name && this->age == p.age)
return true;
return false;
}
string name;
int age;
};
void test01()
{
person p1("汤姆",18);
person p2("汤姆",18);
if (p1 == p2) {
cout << "p1和p2是相等的!" << endl;
}
else cout << "p1和p2是不相等的" << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
4.5.6函数调用运算符
- 函数调用()也可以重载
- 由于重载后使用的方法非常像函数的调用,因此称为仿函数
- 仿函数没有固定写法非常灵活返回的可以是数值也可以是没有返回值。
#include<iostream>
using namespace std;
#include<string>
//函数调用运算符重载
//打印输出类
class myprint {
public:
void operator()(string test)
{
cout << test << endl;
}
};
class add
{
public:
int operator()(int num1, int num2)
{
return num1 + num2;
}
};//设计一个相加类
void print(string p)
{
cout << p << endl;
}
void test01()
{
myprint p;
p("hello world");//使用方法为对象后面直接加括号()。
//被称为仿函数
//此处为函数调用运算符重载
//重载后使用的方法非常像函数的调用,因此称为仿函数
print("hello world");//此处为函数调用
add p3;
int ref = p3(200, 300);
cout << ref << endl;
cout << add()(200,300)<<endl;//这里也是调用了add类中的调用运算符重载运算相当
//于前面的add()可以理解为 一个默认的构造函数 加上后面的() 构成一个仿函数。
}
int main()
{
test01();
system("pause");
return 0;
}
类型+()==匿名对象:当前行执行完了立即被释放
类型+()( )==匿名函数对象
前面的add()可以理解为 一个默认的构造函数 加上后面的() 构成一个仿函数。
调用函数重载的调用形式是
1. 对象名(值,值);
2.类名()(值,值);//匿名函数对象调用 调用运算符重载函数。