1.运算符重载的基本概念
运算符重载:就是对已有的运算符重新进行定义,赋予一种功能,以适应不同的数据类型。
运算符只能运算内置的数据类型,对于自定义的数据类型,不能运算,所以 我们可以重载运算符
语法:定义重载的运算符就像定义函数,只是函数的名字为operator+重载的符号。比如重载+,函数名就是operator+。
重载思路:
1.弄懂运算符的对象个数(个数决定重载函数时的参数)
2.识别运算符的运算对象 :是类的对象,还是其他
类的对象:全局函数实现(不推荐) 成员函数实现(推荐,少一个参数)可以用this指针
其他:只能是全局函数实现
比如我们在成员函数里重载+,对于ob1+ob2,在调用重载函数时:operator+(ob1,ob2)
对于重载函数的一般调用:会先ob1调用函数,为ob1.operator+(ob2),在ob2调用函数为ob1.operator+(ob2.operator+).
考虑到this指针是指向函数首地址,即ob1调用函数的地址。故我们在设计函数时,利用this指针,可以少写一个参数:
operator+(ob2),直接调用重载+的函数会替换为ob1.operator+(ob2).
2.重载加法运算符
这里我们先已简单的重载+运算符为例:
因为普通的加法只对默认的数据类型操作,我们自定义的类的对象是无法让操作符操作的。需要重载运算符实现某些运算。
比如:我们实现person类的相加是将person类里的age相加。
#define _CRT_SECURE_NO_WARNINGS using namespace std; class person { public: person(int age) { this-> age = age; } int age; }; person operator+(person &p1, person &p2) { person p(p1.age+p2.age); return p; } void test01() { person p1(10); person p2(20); person p3 = p1 + p2;// operator+(p1,p2) cout << p3.age << endl; } int main() { test01();//结果为30 return 0; }
这里我们定义了全局函数的+,重载了+运算符,但程序在对运算符调用时,还是会根据参数类型,调用重载后的运算符,还是调用默认的运算符。
同时我们说了运算符重载对于成员函数来说,我们是可以利用this指针的特性实现参数的优化
例如:
using namespace std; class person { public: person(int age) { this-> age = age; } person operator+(person & p2) { person p(this-> age + p2.age); return p; } int age; }; void test01() { person p1(10); person p2(20); person p3 = p1 + p2;// p1.operator+(p2) cout << p3.age << endl; }
我们直接在类里重载+运算符,我们直接p1的this指针调用重载的函数,实现参数的优化。
3.重载运算符<<(全局函数实现)
我们知道默认的cout<<只会输出基本的数据类型,如整形,字符型。想要访问类里的自定义对象,需要重载该运算符。举例:
#include<iostream> #include<string> using namespace std; class person { private: //friend void operator<<(ostream& out, person& lucy); //friend ostream& operator<<(ostream& out, person& lucy); int num; string name; float score; public: person(){} person(int num, string name, float score) :num(num), name(name), score(score){} void showperson(int num, string name, float score) { cout << num << endl; cout << name << endl; cout << score << endl; } };
int main() { person lucy(100, "lucy", 99.5); cout << lucy; return 0; }
这里会直接报错,找不到该运算符的一个函数,该运算符无法对类的对象进行访问操作。
于是我们重载<<
void operator<<(ostream &out,person &lucy)//这里用到引用,还要友元 { out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl; //返回该输出流变量 }
这里的cout的类型为ostream,我们参数直接定义运算符两边的类型的变量,这里直接利用引用,避免指针的麻烦使用。
因为这里访问的变量为私有的数据,如我们在类里还需要定义为友元函数。
我们可以从此理解所谓的运算符重载就是重新定一个函数,让操作符可以实现对数据的操作,而函数名我们用operator+运算符来表示。可以看到这里的函数仅仅是打印出数据,故在使用时,对于操作对象的<<是无法在与endl结合换行,因为该运算符的函数重载中只有打印出数据的功能。
那么如何让他拥有输出流类型的操作,这里我们可以改变重载函数类型:
ostream& operator<<(ostream &out,person &lucy)//这里用到引用,还要友元 { out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl; //返回该输出流变量 return out; }
这里返回的新out是输出流类型,我们在使用时就可以
int main() { person lucy(100, "lucy", 99.5); //这里想要输出数据num,是不可以的,私有数据 //要想访问两种方法:1.利用成员函数 2.重载运算符 //cout << lucy; 直接使用会报错,找不到该运算符的一个函数 //程序运行对于<<进行查找若果类型左边是输出流,右边是person类,则会自动调用重载后的<<,反之,是默认的输出运算符 cout << lucy;//重载后的<<是链式操作,此时这里无法在添加endl. //若想使用输出流类型的写法,函数类型为输出流,因为输出流可以和endl结合。 cout << lucy<<endl; return 0; }
还需要注意的一点关于函数类型后跟&:
在重载运算符时,为了避免不必要的对象拷贝,函数类型后面要跟&符号。这样可以将参数传递给函数时,传递的是对象的引用而不是对象的副本,从而避免了对象的拷贝操作,提高了程序的效率。此外,使用引用还可以避免修改对象的副本而不是实际对象的问题。因此,在重载运算符时,函数类型后面要跟&符号。
4.重载>>(输入)运算符(全局函数实现)
同输出运算符同理:
这里为了观察显眼,参数用的lucy,其实只要是传的类的对象就行。
class person { friend ostream& operator<<(ostream& out, person& lucy); friend void operator>>(istream& in, person& lucy); friend istream& operator>>(istream& in, person lucy); private: int num; string name; float score; public: person() {} person(int num, string name, float score) :num(num), name(name), score(score){} void showperson(int num, string name, float score) { cout << num << endl; cout << name << endl; cout << score << endl; } }; ostream& operator<<(ostream &out,person &lucy){ out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl; }
重载输入运算符:
void operator>>(istream& in, person lucy) { cin >> lucy.num >> lucy.name >> lucy.score; }
主函数调用:
int main() { person lucy; cin >> lucy; cout << lucy<<endl; return 0; }
同样需要注意的是:因为这里的重载函数定义的类型是无返回类型,无法实现链接的功能,比如同时对多个对象输入
int main() { person lucy; person bob; cin >> lucy >> bob; return 0; }
还是改变函数类型
istream& operator>>(istream& in, person& lucy) { cin >> lucy.num >> lucy.name >> lucy.score; return in; }
5. 重载++/--运算符
重载++和--运算符时,有点让人不知所措,因为我们总希望能根据他们出现在对象的前面还是后面而来调用不同的重载函数,例如当看见++a,会调用前置加加重载函数,看见a++,会调用后置加加的重载函数。
++a ; 先自加 在使用
a++;//先使用 在自加
首先理解前置加加与后置加加的实质区别:
.前置加加返回的是引用
.后置加加返回的是对象
其次:
++无论前置还是后置,我们发现他的函数重载时设计的参数将会是一样,但这样就无法确定该调用哪一个,考虑到要让参数不一样而来调用对象的前置函数还是后置函数,于是多利用了一个占位参数
.前置加加调用TYPE& operator++()函数 比如++a,调用operator++(a)
.后置加加调用的是TYPE operator++(int)函数,也就是后置加加多了一个占位参数 .比如a++,调用
operator++(a,int)
.前置加加调用TYPE& operator--()函数 比如--a,调用operator--(a)
.后置加加调用的是TYPE operator--(int)函数,也就是后置加加多了一个占位参数 .比如a--,调用
operator--(a,int).
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string.h> #include <stdlib.h> #include <string> using namespace std; class Myint { public: Myint(int num)//this { this-> num = num; } //重载前置加加 //先加加,再反回this Myint& operator++() { this-> num++; return *this; } //重载后置加加 //先保存当前的this,在加加,返回当前的this Myint operator++(int) { Myint tmp = *this; this-> num++; return tmp; } int num; }; //重载输出流<< ostream& operator<<(ostream & cout, Myint & p) { cout << p.num; return cout; } void test01() { Myint p1(10); cout << p1.num << endl; //operator++(p1) p1.operator++() Myint p2 = p1++; cout << p2 << endl; Myint p3 = ++p1; cout << p3 << endl; } int main() { test01(); return 0; }
同理我们重载一下--
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string.h> #include <stdlib.h> #include <string> using namespace std; class Myint { public: Myint(int num)//this { this-> num = num; } //重载前置加加 //先加加,再反回this Myint& operator--() { this-> num--; return *this; } //重载后置加加 //先保存当前的this,在加加,返回当前的this Myint operator--(int) { Myint tmp = *this; this-> num--; return tmp; } int num; }; //重载输出流<< ostream& operator<<(ostream & cout, Myint & p) { cout << p.num; return cout; } void test01() { Myint p1(10); cout << p1.num << endl; //operator--(p1) p1.operator--() Myint p2 = p1--; cout << p2 << endl; Myint p3 = --p1; cout << p3 << endl; } int main() { test01(); return 0; }
6.重载==运算符
举例:判断person 对象lucy是否等于person对象bob,若相等打印相等,否则打印不相等
class person { friend ostream& operator<<(ostream& out, person& lucy); friend istream& operator>>(istream& in, person& lucy); //friend bool operator==(person& oa, person& ob); private: int num; string name; float score; public: person() {} person(int num, string name, float score) :num(num), name(name), score(score){} void showperson(int num, string name, float score) { cout << num << endl; cout << name << endl; cout << score << endl; } //成员函数重载operator== bool operator==(person &ob) { if (num == ob.num && name == ob.name && score == ob.score) return true; else return false; } }; //全局函数重载operator== //bool operator==(person &oa,person& ob) //{ // if (oa.num == ob.num && oa.name == ob.name && oa.score == ob.score) // return true; // else // return false; //} ostream& operator<<(ostream &out,person &lucy)//这里用到引用,还要友元 { out << lucy.num <<" " << lucy.name<<" " << lucy.score << endl; return out; } istream& operator>>(istream& in, person& lucy) { in >> lucy.num >> lucy.name >> lucy.score; return in; } //判断lucy是否等于bob,若相等打印相等,否则打印不相等 int main() { person lucy; person bob; cin >> lucy>>bob; if (lucy == bob) { cout << "相等" << endl; } else { cout << "不相等" << endl; } cout << lucy<<endl; return 0; }
7.可以重载的运算符
几乎C中所有的运算符都可以重载,但运算符重载的使用时相当受限制的。特别是不能使用C中当前没有意义的运算符(例如用**求幂)不能改变 运算符优先级,不能改变运算符的参数个数。这样的限制有意义,否 则,所有这些行为产生的运算符只会混淆而不是澄清寓语意。
需要注意的是:
对于逻辑与与逻辑或,因为我们无法实现他的短路特性,一般不存在对它们的重载。