題目:如下為類型CMyString的聲明,請為該類型添加賦值運算符函數。 class CMyString { public: CMyString(char *pData=NULL);//構造函數 CMyString(const CMyString& str);//拷貝構造函數 ~CMyString();//析構函數 private: char* m_pData;//數據域,字符指針 };
介紹重載賦值
重載操作符是一些函數,其名字為關鍵字operator后緊跟需要重載的運算符,比如"operator="表示需要重載"="。像任何其他函數一樣,操作符函數有一個返回值和一個形參表。形參表必須具有與該操作符操作數數目相同的形參,但是如果操作符是一個成員函數,它的第一個操作數隱式綁定到this指針,因此形參表中的參數會減少一個。因為賦值運算符必須是類的成員函數,所以this綁定到左操作數的指針。因此,賦值操作符只接受一個形參,且該形參是同一類型的對象,右操作數一般作為const引用傳遞,跟拷貝構造函數相同。
賦值操作符的返回類型應該與內置類型賦值運算的返回類型相同,內置類型的賦值運算返回對左操作數的引用,因此賦值操作符也返回對同一類類型的引用。賦值必須返回對*this的引用,也就是左操作數的引用。一般而言,賦值操作符與復合賦值操作符應返回左操作數的引用。
從上述基礎知識我們知道了重載賦值操作符是一個類的成員函數,這個函數的返回類型是左操作數的引用,也就是*this,並且這個函數的參數是一個同類型的常引用變量。通過上述知識我們可以確定重載操作符函數為:
CMyString& operator=(const CMyString& str);//重載運算符
完整的代碼實現如下:

#include<iostream> #include<stdlib.h> using namespace std; class CMyString { public: CMyString(char *pData=NULL);//構造函數 CMyString(const CMyString& str);//拷貝構造函數 ~CMyString();//析構函數 CMyString& operator=(const CMyString& str);//重載運算符 void Print();//打印字符串 private: char* m_pData;//數據域,字符指針 }; void CMyString::Print() { cout<<m_pData<<endl; } //構造函數 CMyString::CMyString(char *pData) { if(pData==NULL)//如果構造函數的參數為空 { m_pData=new char[1]; m_pData[0]='\0';//初始化字符串,內容為'\0' } else//如果構造函數的參數不為NULL,那么首先求出字符串長度,然后new一個長度為len+1的字符數組 { int len=strlen(pData); m_pData=new char[len+1]; strcpy(m_pData,pData);//字符串拷貝 } } //析構函數 CMyString::~CMyString() { delete[] m_pData; } //拷貝構造函數,拷貝構造函數與構造函數的思路非常類似。 CMyString::CMyString(const CMyString& str) { int len=strlen(str.m_pData); m_pData=new char[len+1]; strcpy(m_pData,str.m_pData); } //重載運算符 CMyString& CMyString::operator=(const CMyString& str) { //如果傳入的參數與當前的實例是同一個實例,則直接返回自身 if(this==&str) return *this; //釋放實例自身已有內存 delete[] m_pData; m_pData=NULL; //在刪除自身內存以后在重新new一個長度為len+1的字符數組,類似拷貝構造函數 int len=strlen(str.m_pData); m_pData=new char[len+1]; strcpy(m_pData,str.m_pData); } void main() { char* text="Hello World!"; CMyString str1(text); CMyString str2; str2=str1; str1.Print(); str2.Print(); system("pause"); }
存在的問題:
在上述代碼中,我們首先釋放實例自身已有內存,
//釋放實例自身已有內存 delete[] m_pData; m_pData=NULL;
然后再去開辟一塊內存空間讓m_pData指向這塊內存空間,
//在刪除自身內存以后在重新new一個長度為len+1的字符數組,類似拷貝構造函數 int len=strlen(str.m_pData); m_pData=new char[len+1];
最后使用strcpy進行字符串賦值。
strcpy(m_pData,str.m_pData);
如果因為內存不足,在new char[len+1]階段拋出異常,那么這時候因為已經釋放了m_pData,導致m_pData指向一個空指針,這樣可能會導致程序崩潰。有兩種方案解決上述問題:
- 先用new分配新內容,然后刪除自己已有內容,最后進行賦值。
- 創建一個臨時實例,交換臨時實例與當前實例的m_pData。代碼如下:

//重載運算符 CMyString& CMyString::operator=(const CMyString& str) { if(this!=&str) { CMyString strTemp(str);//使用構造函數創建一個臨時對象 //交換臨時對象與當前對象的m_pData值 char* pTemp=strTemp.m_pData; strTemp.m_pData=m_pData; m_pData=pTemp; } return *this; }
這樣的一個好處是在運行完if語句以后,因為除了strTemp的作用於,該實例會自動調用析構函數,把strTemp.m_pData所指向的內存釋放掉,而此時strTemp.m_pData指向的是實例原先m_pData指向的內存,並沒有釋放當前指向的pTemp這一塊內存。還有一點是通過構造函數為臨時實例分配內存,如果在new char過程中拋出異常,並沒有改變該實例m_pData所指向的內容,也沒有釋放內存,所以是異常安全性的。