面試題1:賦值運算符函數


題目:如下為類型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);//重載運算符

完整的代碼實現如下:

View Code
#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。代碼如下:
View Code
//重載運算符
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所指向的內容,也沒有釋放內存,所以是異常安全性的。

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM