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; }