C++ 运算符重载

参考文献:

  《C++程序设计》

  推荐转载博文:https://www.cnblogs.com/xiaokang01/p/9166745.html#_label1

.............................................................................................................................................................................

  什么是运算符重载?

  运算符重载的本质是一个函数

  

  运算符重载作用:

  

运算符重载限制:

1) 并不是所有的运算符都可以重载。能够重载的运算符包括:
+  -  *  /  %  ^  &  |  ~  !  =  <  >  +=  -=  *=  /=  %=  ^=  &=  |=  <<  >>  <<=  >>=  ==  !=  <=  >=  &&  ||  ++  --  ,  ->*  ->  ()  []  new  new[]  delete  delete[]

上述运算符中,[]是下标运算符,()是函数调用运算符。自增自减运算符的前置和后置形式都可以重载。长度运算符sizeof、条件运算符: ?、成员选择符.和域解析运算符::不能被重载。

2) 重载不能改变运算符的优先级和结合性。假设上一节的 complex 类中重载了+号和*号,并且 c1、c2、c3、c4 都是 complex 类的对象,那么下面的语句:

c4 = c1 + c2 * c3;

等价于:

c4 = c1 + ( c2 * c3 );

乘法的优先级仍然高于加法,并且它们仍然是二元运算符。

3) 重载不会改变运算符的用法,原有有几个操作数、操作数在左边还是在右边,这些都不会改变。例如~号右边只有一个操作数,+号总是出现在两个操作数之间,重载后也必须如此。

4) 运算符重载函数不能有默认的参数,否则就改变了运算符操作数的个数,这显然是错误的。

5) 运算符重载函数既可以作为类的成员函数,也可以作为全局函数。

将运算符重载函数作为类的成员函数时,二元运算符的参数只有一个,一元运算符不需要参数。之所以少一个参数,是因为这个参数是隐含的。

例如,上节的 complex 类中重载了加法运算符:

complex operator+(const complex & A) const;

当执行:

c3 = c1 + c2;

会被转换为:

c3 = c1.operator+(c2);

通过 this 指针隐式的访问 c1 的成员变量。

将运算符重载函数作为全局函数时,二元操作符就需要两个参数,一元操作符需要一个参数,而且其中必须有一个参数是对象,好让编译器区分这是程序员自定义的运算符,防止程序员修改用于内置类型的运算符的性质。

例如,下面这样是不对的:

  1. int operator + (int a,int b){
  2. return (a-b);
  3. }

+号原来是对两个数相加,现在企图通过重载使它的作用改为两个数相减, 如果允许这样重载的话,那么表达式4+3的结果是 7 还是 1 呢?显然,这是绝对禁止的。

如果有两个参数,这两个参数可以都是对象,也可以一个是对象,一个是C ++内置类型的数据,例如:

  1. complex operator+(int a, complex &c){
  2. return complex(a+c.real, c.imag);
  3. }

它的作用是使一个整数和一个复数相加。

另外,将运算符重载函数作为全局函数时,一般都需要在类中将该函数声明为友元函数。原因很简单,该函数大部分情况下都需要使用类的 private 成员。

上节的最后一个例子中,我们在全局范围内重载了+号,并在 complex 类中将运算符重载函数声明为友元函数,因为该函数使用到了 complex 类的 m_real 和 m_imag 两个成员变量,它们都是 private 属性的,默认不能在类的外部访问。

6) 箭头运算符->、下标运算符[ ]、函数调用运算符( )、赋值运算符=只能以成员函数的形式重载。

  

运算符重载的方法步骤

全局函数、类成员函数方法实现运算符重载步骤
1)要承认操作符重载是一个函数,写出函数名称operator+ ()
2)根据操作数,写出函数参数
3)根据业务,完善函数返回值(看函数是返回引用 还是指针 元素),及实现函数业务
 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Complax
 5 {
 6 private:
 7     int a;
 8     int b;
 9     // 重载友元函数 全局函数 操作符重载
10     friend Complax operator+(Complax &c1, Complax &c2);
11 public:
12     Complax(int a = 0, int b = 0)
13     {
14         this->a = a;
15         this->b = b;
16     }
17     void printC()
18     {
19         cout << "a = " << a << "	b = " << b << endl;
20     }
21     // 2成员函数法 实现 - 运算符重载
22     Complax operator-(Complax &c2)
23     {
24 //        this->a -= c2.a;
25 //        this->b -= c2.b;
26 //        return *this;  // 这一个会改变c1的值,因为是+=
27         Complax temp(this->a - c2.a, this->b -c2.b);
28         return temp;
29 
30     }
31 
32 };
33 // 1全局函数法 实现 + 运算符重载
34 Complax operator+(Complax &c1, Complax &c2)
35 {
36     Complax temp(c1.a+c2.a, c1.b+c2.b);
37     return temp;
38 }
39 /*
40     全局函数,类成员函数方法实现运算符重载步骤
41     1:要承认运算符重载是一个函数, 写出函数名称
42     2: 根据操作数,写出函数参数
43     3:根据业务,完善函数的返回值(看函数返回引用,元素,指针),及实现函数业务
44 */
45 int main()
46 {
47     Complax c1(1, 2), c2(3, 4);
48 
49     // 1全局函数法 实现 - 运算符重载
50     // Complax operator+(Complax &c1, Complax &c2)
51     Complax c3 = c1 + c2;
52     c3.printC();
53 
54     // 2成员函数法 实现 - 运算符重载
55     // Complax operator-(Complax &c2);
56     Complax c4 = c1 - c2;
57     c4.printC();
58 
59     return 0;
60 }
61 
62 运算符重载的两种方法
++重载例子:
 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Complax
 5 {
 6 private:
 7     int a;
 8     int b;
 9     // 1全局函数法 实现 ++ 运算符重载
10     friend Complax& operator++(Complax &c1);  // 这里是返回一个引用注意,前置++
11     friend Complax operator++(Complax &c2, int); // 后置++
12 
13 public:
14     Complax(int a = 0, int b = 0)
15     {
16         this->a = a;
17         this->b = b;
18     }
19 
20     // 前置--
21     // 因为前置返回的是本身,所以返回一个引用
22     // Complax& operator--(Complax &c1)  这个是写错的,注意错在哪里
23     Complax& operator--()
24     {
25         this->a --;
26         this->b --;
27         return *this;
28     }
29 
30     // 后置--  因为后置返回的是一个副本所以不用 返回引用
31     Complax operator--(int)
32     {
33         Complax tem = *this;
34         this->a--;
35         this->b--;
36         return tem;
37     }
38 
39     void printC()
40     {
41         cout << "a = " << a << "	b = " << b << endl;
42     }
43 };
44 
45 
46 
47 // 特别注意 只有成员函数才有 this指针
48 // 1全局函数法 实现 前置++ 运算符重载
49 Complax& operator++(Complax &c1)
50 {
51     c1.a++;
52     c1.b++;
53     return c1;
54 }
55 // 后置++
56 Complax operator++(Complax &c2, int) // int防止与前置++重载而加的占位符
57 {
58     // 因为后置++是使用, 在让c2++所以要定义一个临时变量
59     Complax tem = c2;
60     c2.a++;
61     c2.b++;
62     return tem;
63 }
64 
65 
66 int main()
67 {
68     Complax c1(1, 2), c2(30, 40);
69 
70     // 1全局函数法 实现 前置++ 运算符重载
71     //   Complax& operator++(Complax &c1);
72     ++c1;  // 相当于 operator++(c1);
73     c1.printC();
74 
75     // 2成员函数 实现 前置-- 运算符重载
76     // 相当于c2.operator--();
77     --c2;
78     c2.printC();
79 
80     //  1全局函数法 实现 后置++ 运算符重载
81     // operator++(c2);
82     c2++;
83     //  Complax& operator++(Complax &c1);  前置++
84     //  Complax operator++(Complax &c2, int); // int防止与前置++重载而加的占位符
85     c2.printC();
86 
87     // 2成员函数 实现 后置-- 运算符重载
88     // 相当于c2.operator--();
89     // Complax operator--(int) 函数原型
90     c1--;
91     c1.printC();
92 
93     return 0;
94 }
95 
96 

深度拷贝:

  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdlib>
  4 using namespace std;
  5 
  6 class Name
  7 {
  8 public:
  9 
 10     Name (const char *mp)
 11     {
 12         len = strlen(mp);
 13         p = (char *)malloc(sizeof(len+1)); // 注意细节
 14         strcpy(p, mp);
 15 
 16     }
 17 
 18     //Name obj2 = obj1;
 19     // 解决方案:手工编写拷贝构造函数,使用深拷贝
 20     Name (const Name &obj1)
 21     {
 22         len = obj1.len;
 23         p  = (char *)malloc(sizeof(len+1));
 24         strcpy(p, obj1.p);
 25     }
 26 
 27     // 成员函数 =运算符重载  不能写全局函数
 28     Name& operator=(const Name &obj)
 29     {
 30         // 先释放旧的内存  特别注意
 31         if(p != NULL)
 32         {
 33             delete []p;
 34             len = 0;
 35         }
 36         len = obj.len;
 37         p = new char[len+1];
 38         strcpy(p, obj.p);
 39         return *this;
 40     }
 41     ~Name ()
 42     {
 43         cout << "	析构函数" << endl;
 44         if (p != NULL)
 45         {
 46             free(p);
 47             p = NULL;
 48             len = 0;
 49         }
 50     }
 51     void printName()
 52     {
 53         cout <<"	p = " << this->p << "	 len = " << this->len << endl;
 54     }
 55 private:
 56     char *p;
 57     int len;
 58 };
 59 
 60 
 61 // 对象析构的时候会出现,错误
 62 // 调试存在bug
 63 // 析构的时候会出现二次释放
 64 //默认的拷贝构造函数,如果要对指针进行拷贝,则只是浅拷贝,拷贝过后是两个变量指向
 65 //同一段内存,释放拷贝的obj2后,obj1的指针就是垃圾值,在释放就会出错
 66 
 67 void objmain()
 68 {
 69     Name obj1("aaaa");
 70     Name obj2 = obj1;
 71     Name obj3("sfs");
 72 
 73     //  obj3 = obj1;  // 等号操作 c++编译器会对=进行默认的重载(同样会发生二次释放的问题)
 74     // 就需要手动的写 operator= 函数
 75 
 76     cout << "成员函数=重载  ";
 77     obj3 = obj1;
 78     obj3.printName();
 79     // obj3.operator=(const Name & obj1);
 80     // void operator=(const Name &obj)  // 函数声明
 81     // 如果要执行 obj3 = obj2 = obj1; 就需要把void 改成 Name&
 82     {
 83         obj3 = obj2 = obj1;
 84         obj3.printName();
 85         obj2.printName();
 86         obj1.printName();
 87     }
 88 
 89 }
 90 
 91 int main04()
 92 {
 93     objmain();
 94 }
 95 
 96 // 重载 = 运算符注意点
 97 // 1 先把旧的内存释放
 98 // 2 返回一个引用 obj3 = obj1 = obj2;
 99 // 3数据进行拷贝
100