04运算符重载——运算符与赋值符
1. 基本概念
因此,既可以重载为普通函数,又可以重载为成员函数
解读时:把含运算符的表达式转换成对运算符函数的调用,把运算符的操作数转换成运算符函数的参数。运算符被多次重载时,根据实参的类型决定调用哪个运算符函数。
2. 运算符重载的形式
1 2 3 4
| 返回值类型 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
| #include<iostream> using namespace std;
class Complex { public: double real, imag; Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {} Complex operator-(const Complex &c); }; Complex operator+(const Complex &a, const Complex &b) { return Complex(a.real + b.real, a.imag + b.imag); } Complex Complex::operator-(const Complex &c) { return Complex(real - c.real, imag - c.imag); } int main() { Complex a(4, 4), b(1, 1), c; c = a + b; cout << c.real << "," << c.imag << endl; cout << (a - b).real << "," << (a - b).imag << endl; return 0; }
|
程序输出:
注意:
c = a + b; 等价于c=operator+(a,b);
a-b 等价于a.operator-(b)
3. 赋值运算符的重载
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> #include<string.h> using namespace std; class String { private: char *str;
public: String() : str(new char[1]) { str[0] = 0; } const char *c_str() { return str; }; String &operator=(const char *s); ~String() { delete[] str; } }; String &String::operator=(const char *s) { delete[] str; str = new char[strlen(s) + 1]; strcpy(str, s); return *this; } int main() { String s; s = "Good Luck,"; cout << s.c_str() << endl; s = "Shenzhou 8!"; cout << s.c_str() << endl; return 0; }
|
输出结果:
tips:
C++中有参构造函数,拷贝构造函数和赋值运算符重载的区别和实现
1.拷贝构造函数(复制构造函数)
强调:这里b对象是不存在的,是用a 对象来构造和初始化b的!!
2.有参构造函数
当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存
3.赋值运算符重载
当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值函数。
当没有重载赋值函数(赋值运算符)时,通过默认赋值函数来进行赋值操作
通常大家会对拷贝构造函数和赋值函数混淆,这儿仔细比较两者的区别:
1)拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。
2)一般来说在数据成员包含指针对象的时候,需要考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象
3)实现不一样。拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。赋值函数则是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检察一下两个对象是不是同一个对象,如果是,不做任何操作,直接返回。(这些要点会在下面的String实现代码中体现)
4.浅拷贝、深拷贝
如不定义自己的赋值运算符,那么S1=S2实际上导致 S1.str和 S2.str指向同一地方。

问题:
-
如果S1对象消亡,析构函数将释放 S1.str指向的空间,则S2消亡时还要释放一次,不妥。
-
另外,如果执行 S1 = “other”;会导致S2.str指向的地方被delete
另外,考虑下面语句:
1 2 3
| String s; s = "Hello"; s = s;
|
如果不判定字符串是否完全相同,则导致等号左右两侧同时被析构,导致赋值出错
解决方案:
1 2 3 4 5 6 7 8 9
| String &operator=(const String &s) { if (this == &s) return *this; delete[] str; str = new char[strlen(s.str) + 1]; strcpy(str, s.str); return *this; }
|
注意到,operator = 返回值类型为String&类型,那么这里能不能使用void或String呢?
考虑以下情况:a = b = c; 连等语句的撰写
当函数返回值为引用的时候才能放在等号左边,为了保留原风格的特性,于是使用引用。
则此时:
a = b = c;等价于a.operator=(b.operator=(c));
(a=b)=c;等价于(a.operator=(b)).operator=(c);
1 2 3 4 5
| String(String &s) { str = new char[strlen(s.str) + 1]; strcpy(str, s.str); }
|
5.运算符重载为友元函数
考虑下面程序:
1 2 3 4 5 6 7 8 9 10 11
| class Complex { double real, imag; public: Complex(double r, double i) : real(r), imag(i){}; Complex operator+(double r); }; Complex Complex::operator+(double r) { return Complex(real + r, imag); }
|
经过上述重载后:
但是:
c = 5 + c; //编译出错
所以,为了使得上述的表达式能成立,需要将 + 重载为普通函数
1 2 3 4
| Complex operator+(double r, const Complex &c) { return Complex(c.real + r, c.imag); }
|
但是普通函数又不能访问私有成员,所以,需要将运算符 + 重载为友元。
1 2 3 4 5 6 7 8
| class Complex { double real, imag; public: Complex(double r, double i) : real(r), imag(i){}; Complex operator+(double r); friend Complex operator+(double r, const Complex &c); };
|
项目练习:动态数组
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| #include <iostream> #include <string.h> using namespace std;
class CArray{ int len; int *p; public:
CArray(int s=0); ~CArray(); CArray(CArray &a); void push_back(int ele); int length(); CArray &operator=(const CArray &a); int &operator[](int i); void display() { for(int i=0;i<len;i++) { cout<<p[i]<<" "; } cout<<endl; } }; CArray::CArray(CArray &a) { if(!a.p) { p=NULL; len=0; return; } len=a.len; p=new int[len]; memcpy(p,a.p,sizeof(int)*len); } CArray :: CArray(int s) { len=s; if(len==0)p=NULL; else p=new int [len]; } CArray ::~CArray() { if(p)delete[]p; } void CArray::push_back(int ele) { if(p) { int *tp = new int[len+ 1]; memcpy(tp, p, sizeof(int) * len); delete[] p; p = tp; } else { p=new int[1]; }
p[len]=ele; ++len; } int CArray::length() { return len; } int &CArray::operator[](int i) { return p[i]; } CArray &CArray::operator=(const CArray &a) { if(p==a.p) { return *this; } if(a.p==NULL) { len = 0; if(p)delete []p; p=NULL; return *this; } if(a.len>len) { if(p) delete[] p; p = new int[a.len]; } len=a.len; memcpy(p,a.p,sizeof(int)*a.len); return *this; } int main(void) { CArray a; for (int i = 0; i < 5; ++i) { a.push_back(i); } a.display(); CArray a2, a3; a2 = a; a2.display(); a2 = a3; a3.display(); a[3] = 100; CArray a4(a); a4.display(); return 0; }
|
输出结果:
1 2 3 4
| 0 1 2 3 4 0 1 2 3 4
0 1 2 100 4
|