首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

c++学习笔记(9.操作符重载)

c++学习笔记(9.操作符重载)

本节知识点:1.c++标准库:   a.c++标准库并不是c++语言的一部分,c++标准库是由c++语言编写而成的类库和函数的集合。c++标准库中定义的类和对象都位于std命名空间中,c++标准库的头文件都不带.h后缀,并且c++标准库涵盖了c库的功能,如c库中<name.h>对应c++库中的<cname>
   b.c++标准库预定义了多数常用的数据结构,如:<bitset> <deque> <list> <map> <queue> <set> <stack> <vector>
   c.c++标准库中的cout和cin的使用方式(代码如下):
[cpp] view plaincopy


  • #include <cstdio>
  • #include <iostream>

  • using
    namespace std;  

  • int main()  
  • {  
  •     int a;  
  •     int b;  
  •     printf("put a :\n");  
  •     cin >> a;  
  •     printf("put b :\n");  
  •     cin >> b;  
  •     cout << "sum is : " << a+b << endl;  
  •     return 0;  
  • }   


2.全局函数的操作符重载:
   a.c++中可以通过operator关键字利用函数扩展操作符,即操作符重载,operator的本质是通过函数重载实现操作符的重载,所以操作符重载遵循函数重载的规则。
   b.c++中类的友元的定义:private声明使得类的成员不能被外界访问,但是通过friend关键字可以以友元的形式开放权限。友元访问成员变量,依然是通过对象来访问,跟成员函数还是有很大区别的,普通成员函数可以直接访问成员变量,不需要对象。
代码示例如下:
[cpp] view plaincopy


  • #include <iostream>

  • using
    namespace std;  
  • class test  
  • {  
  • private:  
  •     int a;  
  •     int b;  
  • public:  
  •     test (int a, int b)  
  •     {  
  •         this->a = a;  
  •         this->b = b;  
  •     }  
  •     int geta()  
  •     {  
  •         return a;  
  •     }  
  •     int getb()  
  •     {  
  •         return b;  
  •     }  
  •     /*注意友元可以是任何形式的函数*/
  •     friend test operator+ (test& a1, test& a2); //友元声明
  • };  

  • test operator+ (test& a1, test& a2) //全局操作符重载函数
  • {  
  •     test c(0,0);  
  •     c.a = a1.a + a2.a;  
  •     c.b = a1.b + a2.b;  
  •     return c;  
  • }  
  • int main()  
  • {  
  •     test n1(1,2);  
  •     test n2(2,3);  
  •     test n3(0,0);  
  •     cout << "a is : " << n3.geta() <<endl;  
  •     cout << "b is : " << n3.getb() <<endl;  
  •     n3 = n1 + n2;  
  •     cout << "a is : " << n3.geta() <<endl;  
  •     cout << "b is : " << n3.getb() <<endl;  
  •     return 0;  
  • }  






注意:上代码中的  n1+n2  其实等价于 operator+(n1, n2)
3.类的成员函数的操作符重载:         a.用成员函数重载操作符:比全局函数少一个参数,即左操作数,也是这个类的对象。同时不需要使用friend关键字:
代码如下:
[cpp] view plaincopy


  • #include <iostream>
  • #include <cstdio>
  • using
    namespace std;  
  • class test  
  • {  
  • private:  
  •     int a;  
  •     int b;  
  • public:  
  •     test(int a, int b)  
  •     {  
  •         this->a = a;  
  •         this->b = b;  
  •     }  
  •     test operator+ (test& n2);  
  •     friend test operator+ (test& n1, test& n2);  
  •     friend ostream& operator<<(ostream& out , test& n);  
  • };  
  • ostream& operator<<(ostream& out , test& n)  
  • {  
  •     out << "a is : " << n.a <<endl;  
  •     out << "b is : " << n.b ;  
  •     return out;  
  • }  
  • test test:perator+ (test& n2) //成员函数的操作符重载
  • {  
  •     test n3(0,0);  
  •     n3.a = a + n2.a;  
  •     n3.b = b + n2.b;  
  •     return n3;  
  • }  
  • test operator+ (test& n1, test& n2) //全局函数的操作符重载
  • {  
  •     test n3(0,0);  
  •     n3.a = n1.a + n2.a + 2;  
  •     n3.b = n1.b + n2.b + 2;  
  •     return n3;  
  • }  
  • int main()  
  • {  
  •     test n1(2,2);  
  •     test n2(3,3);  
  •     test n3(0,0);  
  •     //n3 = n1 + n2;          //此处的n3 = n1 + n2 用不了 因为有二义性不知道是调用成员函数还是全局函数
  •     n3 = n1.operator+(n2);   //成员函数的操作符重载 等效于n3 = n1 + n2;
  •     n3 = operator+ (n1, n2); //全局函数的操作符重载 等效于n3 = n1 + n2;
  •     cout << n3 <<endl;       //这个<<的重载 不是成员函数的操作符重载 等价于 operator<<(cout,n3);
  •     operator<<(cout, n3)<<endl;  
  •     cout.operator<< (55);    //这个是<<在ostream类中的重载函数 的调用
  •     return 0;  
  • }   

注意:成员函数的操作符重载,op1 符号 op2 等价于 op1.operator 符号 (op2)   所以说op1必须是这个类的对象
           全局函数的操作符重载,op1 符号 op2 等价于 operator符号(op1, op2)   所以说op1和op2可以是任何类型

4.成员函数和全局函数的操作符重载适用情况:   a.如果左操作数不是一个类的对象,只能用全局函数的操作符重载
   b.当无法修改左操作数的类时,使用全局函数进行重载就像前面重载的<<符号一样,因为不能修改ostream这个类,所以就只能使用全局函数的操作符重载
   c.其实大量的成员函数的操作符重载,都可以被全局函数的操作符重载代替,但是对于=赋值操作符、[ ]数组运算操作符、()函数调用操作符、->指针操作符只能通过成员函数进行重载。(这四个操作符是c++编译器中的特殊规定只能使用成员函数进行操作符重载,不要问为什么,违反了编译会出错)
   d.c++编译器会为每一个类提供一个默认的赋值操作符,默认的赋值操作符只是做简单的值的复制。当类中存在指针成员变量(尤其是指向new出来的内存的指针)时,就需要重载赋值操作符了,否则在析构函数中delete的时候,就会对同一个地址delete两次!!!
代码如下:
[cpp] view plaincopy


  • #include <iostream>

  • using
    namespace std;  

  • class test  
  • {  
  • private:  
  •     int a;  
  •     int b;  
  • public:  
  •     test(int a, int b)  
  •     {  
  •         this->a = a;  
  •         this->b = b;  
  •     }  
  • /*  test(const test& n)
  •     {
  •         cout<<"hhhhhhh"<<endl;
  •     }*/
  •     test& operator= (test& n2);  
  •     friend ostream& operator<<(ostream& out , test& n);  
  • };  
  • ostream& operator<<(ostream& out , test& n)  
  • {  
  •     out << "a is : " << n.a <<endl;  
  •     out << "b is : " << n.b ;  
  •     return out;  
  • }  
  • test& test:perator= (test& n2)  
  • {  
  •     a = n2.a + 10;  
  •     b = n2.b + 10;  
  •     return *this;  
  • }  
  • /**********************************************************************  
  • 这里面有一个有意思的东西,就是编译器提供的类的拷贝构造函数中,
  • 有一个类对类赋值的过程  test a = b 是调用拷贝构造函数  

  • 编译器同时对类也提供了一个=赋值运算符的重载成员函数,负责简单
  • 的类之间的赋值,如test a; a = b;这个过程就调用了这个重载的函数

  • 但是他俩都有一个性质,就是如果用户定义了这个函数,编译器提供的
  • 函数就失效了  
  • **********************************************************************/
  • int main()  
  • {  
  •     test a(2,2);  
  •     test b = a; //调用拷贝构造函数中的赋值过程
  •     cout << b;  

  • //  test b = test(0,0);
  • //  b = a;      //调用编译器提供的 =赋值运算符的重载函数
  •     test c = test(9,9);  
  •     b = c;  
  •     cout << b;  
  •     return 0;  
  • }   




注意:第一,c++编译器会为类提供一个拷贝构造函数,提供一个赋值操作符重载函数。两个默认函数的功能相同,都是对类的成员变量进行简单的复制。
            第二,但是两个函数之间是相互独立的,有各自的应用场景,如在test b = a; 类定义初始化的时候,此时调用的是类的拷贝构造函数。还有就是在函数参数为类的时候void fun(test a),在函数调用进行参数传递的时候,调用拷贝构造函数,实现实虚参之间的传递。如在b = a 两个类直接赋值的时候,调用的是默认赋值操作符重载函数。
            第三,就是这两个默认提供的函数,一旦用户自己定义了,原来c++编译器提供的默认函数就会失效。
5.++操作符的重载:   a.c++中通过一个占位参数来区分前置运算和后置运算,且++运算符的重载即可以是全局函数也可以是成员函数,代码如下:
[cpp] view plaincopy


  • #include <iostream>
  • using
    namespace std;  
  • class test  
  • {  
  • private:  
  •     int a;  
  •     int b;  
  • public:  
  •     test(int a, int b)  
  •     {  
  •         this->a = a;  
  •         this->b = b;  
  •     }  
  •     friend ostream& operator<<(ostream& out , test& n);  
  •     friend test operator++ (test& a, int);  
  •     friend test& operator++ (test& a);  
  • };  
  • ostream& operator<<(ostream& out , test& n)  
  • {  
  •     out << "a is : " << n.a <<endl;  
  •     out << "b is : " << n.b ;  
  •     return out;  
  • }  
  • test operator++ (test& a, int) //a++ 后置运算
  • {  
  •     test ret = a;  
  •     a.a++;  
  •     a.b++;  
  •     return ret;  
  • }  
  • test& operator++ (test& a)  //++a 前置运算
  • {  
  •     ++a.a;  
  •     ++a.b;  
  •     return a;     
  • }  
  • int main()  
  • {  
  •     test a(0,0);  
  •     test b(9,9);  
  •     test c(8,8);  
  •     b = a++;  

  •     cout << b << endl;  
  •     cout << a << endl;  

  •     cout << "~~~~~" << endl;  

  •     c = ++a;  
  •     cout << c << endl;  
  •     cout << a << endl;  
  •     return 0;  
  • }   




注意: operator++ (test& a, int) 为a++ 后置运算,operator++(test& a) 为++a 前置运算

6.&&和||操作符的重载:   a.特别注意不要对&&和||操作符进行重载,因为正常的&&和||内置实现了短路规则,但是操作符重载是靠函数重载来完成的,操作数作为函数参数传递,c++中的函数参数都会被求值,无法实现短路规则。
代码如下:
[cpp] view plaincopy


  • #include <cstdlib>
  • #include <iostream>

  • using
    namespace std;  

  • class Test  
  • {  
  •     int i;  
  • public:  
  •     Test(int i)  
  •     {  
  •         this->i = i;  
  •     }  

  •     Test operator+ (const Test& obj)  
  •     {  
  •         Test ret(0);  

  •         cout<<"Test operator+ (const Test& obj)"<<endl;  

  •         ret.i = i + obj.i;  

  •         return ret;  
  •     }  

  •     bool operator&& (const Test& obj)  
  •     {  
  •         cout<<"bool operator&& (const Test& obj)"<<endl;  

  •         return i && obj.i;  
  •     }  
  • };  

  • int main(int argc, char *argv[])  
  • {  
  •     int a1 = 0;  
  •     int a2 = 1;  

  •     if( a1 && (a1 + a2) )  
  •     {  
  •         cout<<"Hello"<<endl;  
  •     }  

  •     Test t1 = 0;  
  •     Test t2 = 1;  

  •     if( t1 && (t1 + t2) ) // 正常来说如果 t1为0  t1+t2是不应该被执行的  
  •     {  
  •         cout<<"World"<<endl;  
  •     }  

  •     return 0;  
  • }  




注意:正常来说 上面的 t1+t2 是应该被短路的,不应该被执行的,但是由于t1+t2实质上是函数的参数,所以被执行了,违背了短路规则!!!
7.本节总结(几个常出面试题):

   a. =赋值操作符 、[]数组操作符 、()函数调用操作符 、->指针操作符,这个四个操作符只能通过成员函数进行重载
   b.++操作符通过一个int参数进行前置与后置的重载
   c. c++中不要重载&&和||操作符
返回列表