0825
运算符重载:
小目录
- 1.加号运算符
+
重载—成员函数实现、全局函数实现,返回值是一个新的类对象 - 2.关系运算符
==
和!=
重载—成员函数实现、全局函数实现,返回值是bool
- 3.递增运算符
++
重载—成员函数实现、全局函数实现,前置++返回的是对象本身的引用
,后置++返回的是对象本身的副本
参考链接:
①C++ 递增运算符:前置++和后置++的区别(用前置):迭代器和其他模板对象应该使用前缀形式的自增 (++i) 、自减 (–i) 运算符,因为前置自增 (++i) 通常要比后置自增 (i++) 效率更高;
②第五章的5.1.7前缀格式和后缀格式—i++ 和 ++i 的效率(内置类型和用户自定义类型) - 4.左移运算符
<<
重载—只能全局函数实现,返回值是cout
- 5.赋值运算符
=
重载—只能成员函数实现,返回*this对象本身 这个有可能涉及到深拷贝与浅拷贝问题,当成员变量在堆区时就会涉及到。 - 6.函数调用运算符
()
重载—仿函数/函数对象
概念+实现方式
概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
例如:加号运算符重载的作用就是实现两个自定义数据类型
相加的运算。在运算符重载的这部分代码中实现类内对应的各成员变量的相加操作,比如p1.age + p2.age
,在main函数中就可以直接对两个类进行相加操作了。
实现的方式有两种:
利用成员函数
重载
利用全局函数
重载
加号运算符重载
1.加号运算符重载
最终要实现 类1 + 类2 或者 类1 + val //这里的类1和类2是自定义数据类型,val是int或者double等类型。
//①加号运算符重载
class Operate1 {
public:
//成员变量
int a;
double b;
public:
//构造函数
Operate1() {}
Operate1(int a, double b) {
this->a = a;
this->b = b;
}
//成员函数:返回值为Operate1类,即自定义数据类型------实现类内对应的各成员变量的相加操作
Operate1 operator+(Operate1& p1) {//相当于对象p本身(this) + 参数p1
Operate1 temp;
temp.a = this->a + p1.a;
temp.b = this->b + p1.b;
return temp;
}
//成员函数:设置类中的成员变量
void setInfo(int m, double n) {
a = m;
b = n;
}
//成员函数:输出类中的成员变量
void getInfo() {
cout << this->a << " " << this->b << endl;
}
};
//全局函数1:返回值为Operate1类,即自定义数据类型---实现类内对应的各成员变量的相加操作
Operate1 operator+(const Operate1& p1,const Operate1& p2) {//相当于参数p1 + 参数p2
Operate1 temp;
temp.a = p2.a + p1.a;
temp.b = p2.b + p1.b;
return temp;
}
//全局函数2:返回值为Operate1类,即自定义数据类型---实现类内各成员变量和val的相加操作
Operate1 operator+(const Operate1& p1, int val) {//相当于参数p1 + 参数val
Operate1 temp;
temp.a = p1.a + val;
temp.b = p1.b + val;
return temp;
}
int main() {
//①加号运算符重载
Operate1 ope1(2,2.56);
Operate1 ope2(3,3.67);
Operate1 ope3;
//ope3 = ope2.operator+(ope1);//成员函数实现
ope3 = ope1 + ope2;//两个类直接相加
ope3.getInfo();//5 6.23
ope2.setInfo(3, 3.57);
//ope3 = operator+(ope1, ope2);//全局函数1实现
ope3 = ope1 + ope2;//两个类直接相加
ope3.getInfo();//5 6.13
//ope3 = operator+ (ope2, 10);//全局函数2实现
ope3 = ope2 + 10;//一个类和一个变量val直接相加
ope3.getInfo();//13 13.57
system("pause");
return 0;
}
左移运算符(<<)重载
2.左移运算符(<<)重载
最终要实现 cout << 类 << endl;//这里的类是自定义数据类型
无法利用成员函数
重载左移运算符(<<);
因为成员函数最多只能写成
void operator<<(cout){
//简化成对象p本身(this) << cout 不是我们想要的cout << 对象p
…
}
所以只能
利用全局函数
实现左移运算符(<<)的重载:
void operator<<(cout , 对象p1){
//简化成 cout << 对象p1 这正是我们想要的
…
}
这其中对象p1是自定义数据类型,cout是ostream(输出流)对象
,cout也可以写成其他名称,比如写成out,它只是一个输出流的对象的名称,类似于Person类的一个对象为p1。
示例:
//左移运算符(<<)重载(只能用全局函数实现)
class Operate2 {
public://①
int a;
int b;
};
//全局函数//②
void operator<<(ostream& cout, Operate2& p) {//③
cout << p.a << " " << p.b << endl;
}
int main() {
//左移运算符(<<)重载(只能用全局函数实现)
Operate2 ope4;
ope4.a = 10;
ope4.b = 20;
cout << ope4;//10 20 这样就实现了直接输出一个自定义的类对象ope4
// << endl;④
system("pause");
return 0;
}
改进
四个可以改进的点:
①一般成员变量都是private型,带来的改变就是需要用到友元(friend)
②全局函数有类内实现和类外实现(1.3.8 类模板与友元),类内实现比较简单
③全局函数的返回值改为Operate2& return cout;这样就实现了链式编程思想(4.3.2 this指针用途②)
④第三点改进之后,就能实现cout << ope4 << endl;还可以再往后追加,例如cout << ope4 << “hello world” << endl;
改进过之后的程序(标注的①②③就是改过的地方):
//左移运算符(<<)重载(只能用全局函数实现)
class Operate2 {
//①全局函数类内实现
//friend ostream& operator<<(ostream& cout, Operate2& p);//可以在类内声明,类外实现
friend ostream& operator<<(ostream& cout, Operate2& p) {//也可以类内直接声明+实现
cout << p.a << " " << p.b << endl;
return cout;//返回引用
}
private://②
int a;
int b;
public:
//构造函数
Operate2(int a, int b) {
this->a = a;
this->b = b;
}
};
//全局函数(挪到类内去实现)
//ostream& operator<<(ostream& cout, Operate2& p) {
//
// cout << p.a << " " << p.b << endl;
// return cout;//返回引用
//}
int main() {
//左移运算符(<<)重载(只能用全局函数实现)
Operate2 ope4(10,20);
cout << "实现打印类Operate2:" << ope4 << "cout链式编程" << endl;//10 20 ③这样就实现了直接输出一个自定义的类对象ope4
system("pause");
return 0;
}
递增运算符(++)重载
3.递增运算符(++)重载
最终要实现 类++ 和 ++类,这里的类是自定义数据类型
实现之后要输出,即cout << 类 << endl;
,所以要先实现②左移运算符重载,然后再实现递增运算符(++)重载.(注意和main函数中写的内置的自加运算符测试
类比着来理解)
示例:
//③递增运算符(++)重载:
//最终要实现 `类++和 ++类,这里的类是自定义数据类型`
//实现之后要输出,即`cout << 类 << endl; `,所以要先实现2.左移运算符重载,然后再实现递增运算符(++)重载.
class MyInt {
//全局函数实现左移运算符重载(类内实现)---打印类的成员变量num
friend ostream& operator<<(ostream& cout, MyInt myint) {//const MyInt& myint 也可以写成 MyInt myint,注意这里和左移运算符中的重载写法不太一样
cout << myint.num;
return cout;
}
public:
//构造函数
MyInt() {
num = 0;//设置初值
}
//++重载:类++和++类
//++类:
MyInt& operator++() {//为什么要返回引用,是为了一直对一个数据(这里是自定义数据---类)进行递增操作
num++;//先++
return *this;//再返回对象本身this
}
//类++:
MyInt operator++(int) {//这里的int代表占位参数,可以用于区分前置和后置递增,
//141和134两行属于重载,重载的条件是参数的个数、顺序、类型不同,
//所以就加了个int,而且编译器只认int,其他的都不行,只有int可以区分开类++和++类
MyInt temp = *this;//先记录对象本身this
num++;//后++
return temp;//最后将记录的返回,注意这里返回的是值,不是引用&
}
private:
int num;
};
int main() {
//③递增运算符(++)重载
//内置的递增运算符测试:
int a = 0;
//内置的前置递增运算符返回的是变量本身,即一直往一个数据a上做累加操作,所以第134行要返回对象本身
cout << ++a << endl;//1
cout << a << endl;//1
cout << ++(++a) << endl;//3
cout << a << endl;//3
//内置的后置递增运算符返回的是一个数,所以不能对(a++)再进行自加操作
//cout << (a++)++ << endl;//报错
//只能通过以下的方式来
a = 0;
cout << a++ << endl;//0
cout << a << endl;//1
//运算符重载测试:
MyInt myint;
cout << "先实现类MyInt的打印:" << myint << "\n然后实现类++和++类:" << endl;//0
//前置++
cout << "++类:" << ++(++myint) << endl;//2
cout << "打印类MyInt:" << myint << endl;//2
//后置++
cout << "类++:" << myint++ << endl;//2
cout << "类++:" << ((myint++)++)++ << endl;//结果是3不是4也不是5,所以后置++的链式没有意义,和往上18行是一个道理
cout << "打印类MyInt:" << myint << endl;//4
system("pause");
return 0;
}
赋值运算符(=)重载
4.赋值运算符(=)重载
c++编译器至少给一个类添加4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
赋值运算符 operator=, 对属性进行值拷贝
,简单的值拷贝
如果类的成员变量在堆区,做赋值操作时就会出现深拷贝与浅拷贝
问题。
参考4.2.5 深拷贝与浅拷贝
示例1(涉及到拷贝堆区数据,即深拷贝):
//④赋值运算符重载
class Person4 {
public:
//成员变量
int* age;
//构造函数
Person4(int age) {
this->age = new int(age);
}
拷贝构造函数
//Person4(Person4& p) {
// this->age = p.age;
//}
//析构函数
~Person4() {
if (age != NULL) {
delete age;
age = NULL;
}
}
//赋值运算符重载
Person4& operator=(Person4& p1) {
if (age != NULL) {
delete age;
age = NULL;
}
//this->age = p1.age;//浅拷贝,这是编译器提供的拷贝构造
this->age = n