C++运算符重载——重载特殊运算符
1、重载赋值运算符=
赋值运算符用于同类对象间的相互赋值。赋值运算符只能被重载为类的非静态成员函数,不能重载为友元函数和普通函数。
对于用户自定义的类而言,如果没有重载赋值运算符,那么C++编译器会为该类提供一个默认的重载赋值运算符成员函数。
默认赋值运算符的工作方式是按位对拷,将等到右边对象的非静态成员拷贝给等号左边的对象。
重载赋值运算符函数必须是public的,否则会编译错误,因为用户定义了重载赋值运算符函数,编译器就不会提供默认的。
在类中重载的赋值运算符函数不能被继承!
通常情况下编译器提供的默认重载赋值运算符函数能够解决对象间赋值的问题,但是当类中含有指针数据成员时,容易引起指针悬挂的问题,
所以这种情况下有必要进行赋值运算符重载。
举一个例子,下面的程序使用默认的重载赋值运算符函数,在Linux下运行会检测到double free的问题,其实还有一个内存泄漏的问题(使用valgrind工具检测),即指针悬挂。
原因是对象之间赋值时,对象s2的指针成员pstr的值被赋值成对象s1的指针成员pstr的值,这时对象s2中申请的堆空间就被遗忘了,程序结束时没有人释放,
同时s2对象析构时,释放的是所s1申请的堆空间,s1对象析构时,就产生了2次释放的问题。
#include <cstring>
#include <iostream> using namespace std; class MyString { public: MyString(const char* str){count = strlen(str);pstr = new char[count+1];strcpy(pstr, str);} ~MyString(){delete []pstr;} void display(void){cout<<pstr<<endl;} private: char *pstr; int count; }; int main(void) { MyString s1("Hello");s1.display(); MyString s2("World");s2.display(); s2 = s1;s2.display();//glibc detected : double free or corruption return 0; }
/** valgrind 结果
Invalid free() / delete / delete[] / realloc()
LEAK SUMMARY:
definitely lost: 6 bytes in 1 blocks
**/
下面使用自定义赋值运算符重载来解决内存泄漏和2次释放的问题。
#include <cstring> #include <iostream> using namespace std; class MyString { public: MyString(const char* str){count = strlen(str);pstr = new char[count+1];strcpy(pstr, str);} ~MyString(){delete []pstr;} void display(void){cout<<count<<":"<<pstr<<endl;} //operator overload : = MyString& operator=(MyString& ref) { if (this != &ref) { delete []pstr; count = ref.count; pstr = new char[count+1]; strcpy(pstr, ref.pstr); } return (*this); } private: char *pstr; int count; }; int main(void) { MyString s1("Hello");s1.display(); MyString s2("World");s2.display(); s2 = s1;s2.display();//OK return 0; }
2、重载下标运算符[]
下标运算符只能被重载为类的非静态成员函数,不能重载为友元函数和普通函数。
由于[]既可以作为左值又可以作为右值,所以重载下标运算符函数通常返回引用。
于其他运算符重载有一点不同的是:[]重载的功能是多样的,完全取决于用户的定义,但是通常是数组相关的。
重载的下标运算符函数只能有一个参数,即使用的方式只能是 Aclss[para],不能没有参数或者是Aclss[para1, para2]多参数的形式。
例子:通过重载下标运算符,可以很方便的取得和修改类中的数组。
#include <iostream> using namespace std; class MyArrary { public: MyArrary(int a1,int a2,int a3, int a4){arr[0]=a1;arr[1]=a2;arr[2]=a3;arr[3]=a4;} //operator overload : [] int& operator[](int num)//返回引用才能进行赋值操作 { if ((num < 0) || (num > 4)) { cout <<"error:"; num = 0; } return arr[num]; } private: int arr[4]; }; int main(void) { MyArrary ma(1,2,3,4); cout << ma[0]<<endl; //取值 ma[0] = 5; //赋值 cout << ma[5]<<endl; return 0; }
3、重载括号运算符()
括号运算符只能被重载为类的非静态成员函数,不能重载为友元函数和普通函数。
由于()既可以作为左值又可以作为右值,所以重载括号运算符函数通常返回引用。
重载括号运算符函数的参数个数没有限制,甚至没有参数都可以。
例子:通过重载括号运算符,可以很方便的取得和修改类中的数组。
#include <iostream> using namespace std; class DArrary { public: DArrary(int num){int i = 0; for (i = 0; i< 9;i++) arr[i/3][i%3] = num++;} void display(void){int i = 0;for (i = 0; i< 9;i++) cout<<arr[i/3][i%3]<<" ";cout<<endl;} //operator overload : () Multiple Parameters int& operator()(int a, int b) { return arr[a][b]; } //operator overload : () Singal Parameter int& operator()(int a) { return arr[a/3][a%3]; } //operator overload : () None Parameter int& operator()(void) { return arr[0][0]; } private: int arr[3][3]; }; int main(void) { DArrary arr(1);arr.display(); cout << arr(0,0) << endl; //取值 cout << arr(2,2) << endl; //取值 arr(0,0) = 11; //赋值 arr(2,2) = 22; //赋值 cout << arr(0,0) << endl; cout << arr(2,2) << endl; cout << arr(7) << endl; //取值 arr(7) = 33; //赋值 cout << arr(7) << endl; cout << arr() << endl; arr() = 111; arr.display(); return 0; }