为什么要对运算符进行重载
C++预定义中的运算符的操作对象只局限于基本的内置数据类型,但是对于我们自定义的类型(类)是没有办法操作的。但是大多时候我们需要对我们定义的类型进行类似的运算,这个时候就需要我们对这么运算符进行重新定义,赋予其新的功能,以满足自身的需求。
运算符重载的实质
运算符重载的实质就是函数重载或函数多态。运算符重载是一种形式的C++多态。目的在于让人能够用同名的函数来完成不同的基本操作。要重载运算符,需要使用被称为运算符函数的特殊函数形式,运算符函数形式:operator p(argument-list)//operator 后面的’p’为要重载的运算符符号。
语法如下:
1 2 3 4
| 返回值类型 operator 运算符符号(参数表) { 函数体 }
|
这里的 ‘operator运算符’ 相当于函数名
加法运算符的重载
第一种:通过全局函数对加号进行重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include<iostream> using namespace std;
class Person { public: int a; int b; };
Person operator+(Person &p1,Person &p2) { Person temp; temp.a=p1.a+p2.a; temp.b=p1.b+p2.b; return temp; }
int main() { Person p1; p1.a=10; p1.b=10; Person p2; p2.a=20; p2.b=15; Person p3; p3=p1+p2; cout<<"p3.a="<<p3.a<<endl; cout<<"p3.b="<<p3.b<<endl; return 0; }
|
第二种:通过成员函数对加号进行重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include<iostream> using namespace std;
class Person { public: int a; int b; Person operator+(Person &p) { Person temp; temp.a=this->a+p.a; temp.b=this->b+p.b; return temp; } }; int main() { Person p1; p1.a=10; p1.b=10; Person p2; p2.a=20; p2.b=15; Person p3; p3=p1+p2; cout<<"p3.a="<<p3.a<<endl; cout<<"p3.b="<<p3.b<<endl; return 0; }
|
左移运算符的重载
利用成员函数重载左移运算符 p.operator<<(cout)
简化版本p<<cout
,所以我们一般不会利用成员函数重载<<运算符,因为无法实现cout在左侧
通过全局函数重载左移运算符
void operator<<(cout,p)
的本质是operator<<(cout,p)
简化为cout<<p
,从而实现cout在左侧
cout属于ostream类的一个对象,叫做标准输出流对象
初级代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include<iostream> using namespace std; class Person { public: int a; int b; };
void operator<<(ostream &cout,Person &p) { cout<<"p1.a="<<p.a<<endl; cout<<"p1.b="<<p.b<<endl; }
int main() { Person p1; p1.a=10; p1.b=20; cout<<p1; return 0; }
|
注意:以上代码cout<<p1;
不能再追加endl;
因为此时函数返回值为空,所以我们要加上一个返回数据类型,也就是ostream &
,这里同样可以按照引用来理解。
改进后如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include<iostream> using namespace std; class Person { public: int a; int b; };
ostream & operator<<(ostream &cout,Person &p)
{ cout<<"p1.a="<<p.a<<endl; cout<<"p1.b="<<p.b<<endl; return cout; }
int main() { Person p1; p1.a=10; p1.b=20; cout<<p1<<endl<<"Hello world!"<<endl; return 0; }
|
但是通常情况下,我们类中的成员属性设置成私有化,但是私有化之后重载运算符的函数不能够正常访问类中的成员变量,而利用我们以前set get函数的方式又太麻烦了,那么我们可以用友元函数来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include<iostream> using namespace std; class Person { friend ostream & operator<<(ostream &cout,Person &p); private: int a; int b; public: void setAB(int aa,int bb) { a=aa; b=bb; } };
ostream & operator<<(ostream &cout,Person &p) { cout<<"p1.a="<<p.a<<endl; cout<<"p1.b="<<p.b<<endl; return cout; }
int main() { Person p1; p1.setAB(10,20); cout<<p1<<endl<<"Hello world!"<<endl; return 0; }
|
总结:重载左移运算符配合友元可以实现输出自定义数据类型
递增运算符的重载
用成员函数实现
*this的解释:
*表示的解引用,那么*this
就很好理解了,就是指针的内容,即对象本身。return *this返回的是当前对象的克隆或者本身(若返回类型为A, 则是克隆, 若返回类型为A&, 则是本身 )
下面用的是成员函数进行的重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include<iostream> using namespace std; class MyInteger { friend ostream& operator<<(ostream &cout,MyInteger i); private: int a=0; public: MyInteger& operator++() { a++; return *this; } MyInteger operator++(int) { MyInteger temp=*this; a++; return temp; } };
ostream& operator<<(ostream &cout,MyInteger i) { cout<<i.a; return cout; }
int main() { MyInteger myint; cout<<++(++myint)<<endl; cout<<myint<<endl; cout<<"-----------------"<<endl; cout<<myint++<<endl; cout<<myint<<endl; return 0; }
|
注意:前置++返回的是引用,后置++返回的是值
用全局函数实现
前置++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include<iostream> using namespace std; class MyInteger { friend ostream& operator<<(ostream &cout,MyInteger i); friend MyInteger operator++(MyInteger &i); private: int a=0; public:
};
ostream& operator<<(ostream &cout,MyInteger i) { cout<<i.a; return cout; }
MyInteger operator++(MyInteger &i) { i.a=i.a+1; return i; }
int main() { MyInteger myint;
cout<<"----------------"<<endl; ++myint; cout<<++myint; return 0; }
|
后置++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include<iostream> using namespace std; class MyInteger { friend ostream& operator<<(ostream &cout,MyInteger i); friend MyInteger operator++(MyInteger &i,int); private: int a=0; };
ostream& operator<<(ostream &cout,MyInteger i) { cout<<i.a; return cout; }
MyInteger operator++(MyInteger &i,int) { MyInteger temp=i; i.a=i.a+1; return temp; }
int main() { MyInteger myint; cout<<"----------------"<<endl; cout<<myint++<<endl; cout<<myint; return 0; }
|
人生建议:重载++运算符用成员函数进行
赋值运算符重载
什么时候用到赋值运算符重载?
答:当浅拷贝不适用时,要用赋值运算符重载来进行深拷贝,以避免堆区内存二次删除。
下面用成员函数对赋值运算符进行重载操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include<iostream> using namespace std;
class Person { private: int *age; public: int getAge() { return *age; } Person(int a) { age=new int(a); } ~Person() { if(age!=NULL) { delete age; age=NULL; } } Person& operator=(Person &a) { if(age!=NULL) { delete age; age=NULL; } age=new int(*a.age); return *this; } };
int main() { Person p1(18); Person p2(20); Person p3(30); cout<<p1.getAge()<<" "<<p2.getAge()<<" "<<p3.getAge()<<endl; p3=p2=p1; cout<<p1.getAge()<<" "<<p2.getAge()<<" "<<p3.getAge()<<endl; return 0; }
|
总结:重点就是深拷贝和浅拷贝的知识,目的是为了防止浅拷贝造成堆区内存二次释放。
关系运算符重载
用成员函数重载“==”和“>”其余类似,不再赘述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include<iostream> #include<cstring> using namespace std;
class Person { private: int age; string name; public: Person(string n,int a) { name=n; age=a; } bool operator==(Person &p) { if(name==p.name&&age==p.age) { return true; } return false; } bool operator>(Person &p) { if(age>p.age) { return true; } return false; } };
int main() { Person p1("Tom",18); Person p2("Tom",18); if(p1==p2) { cout<<"p1==p2"<<endl; } else { cout<<"p1!=p2"<<endl; } Person p3("Tom",19); if(p1>p3) { cout<<"p1>p3"<<endl; } else { cout<<"p1<p3"<<endl; } return 0; }
|
函数调用运算符重载
- 函数调用运算符()也可以重载
- 由于重载后使用的方式非常像函数的调用,因此称仿函数
- 仿函数没有固定的用法,非常灵活
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include<iostream> #include<string> using namespace std;
class Print { public: void operator()(string a) { cout<<a<<endl; } }; void b(string n) { cout<<n; } int main() { Print a; a("print"); b("Print"); return 0; }
|
不过这里比较重要的是一个匿名函数对象的概念,我们将上面的代码稍作修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<iostream> #include<string> using namespace std;
class Print { public: void operator()(string a) { cout<<a<<endl; } }; void b(string n) { cout<<n<<endl; } int main() { Print a; a("print"); b("Print"); Print()("Print again"); return 0; }
|