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 的成员变量。
将运算符重载函数作为全局函数时,二元操作符就需要两个参数,一元操作符需要一个参数,而且其中必须有一个参数是对象,好让编译器区分这是程序员自定义的运算符,防止程序员修改用于内置类型的运算符的性质。
例如,下面这样是不对的:
- int operator + (int a,int b){
- return (a-b);
- }
+
号原来是对两个数相加,现在企图通过重载使它的作用改为两个数相减, 如果允许这样重载的话,那么表达式4+3
的结果是 7 还是 1 呢?显然,这是绝对禁止的。
如果有两个参数,这两个参数可以都是对象,也可以一个是对象,一个是C ++内置类型的数据,例如:
- complex operator+(int a, complex &c){
- return complex(a+c.real, c.imag);
- }
它的作用是使一个整数和一个复数相加。
另外,将运算符重载函数作为全局函数时,一般都需要在类中将该函数声明为友元函数。原因很简单,该函数大部分情况下都需要使用类的 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 << "\tb = " << 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 << "\tb = " << 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 << "\t析构函数" << endl; 44 if (p != NULL) 45 { 46 free(p); 47 p = NULL; 48 len = 0; 49 } 50 } 51 void printName() 52 { 53 cout <<"\tp = " << this->p << "\t 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