#include<iostream>
#include<string.h>
class String
{
public:
String(const char*str=NULL);
String(const String&another);
~String();
String& operator=(const String &str);
private:
char* m_pdata;
};
String::String(const char*str)
{
if (str==NULL)
{
m_pdata=new char[1];
m_pdata[0]='\0';
}
else
{
m_pdata=new char[strlen(str)]+1;
strcpy(m_pdata,str);
}
}
String::String(const String& another)
{
m_pdata=new char[strlen(another.m_pdata)+1];
strcpy(m_pdata,another.m_pdata);
}
String & String::operator=(const String &str)
{
if(this==&str)
return *this;
delete[]m_pdata;
m_pdata=NULL;
m_pdata=new char[strlen(str.m_pdata)+1];
strcpy(m_pdata,str.m_pdata);
return *this;
}
String::~String()
{
delete[]m_pdata;
}
賦值運算符的實現(xiàn)需要注意以下幾點:
- 連續(xù)賦值。把返回值聲明為該類型的引用,并在函數(shù)結(jié)束前返回實例自身的引用*this。
- 避免無謂消耗。參數(shù)類型聲明為常量引用,如果傳入的不是引用而是實例,那么從形參到實參會調(diào)用一次復(fù)制構(gòu)造函數(shù)。聲明為引用避免這樣的無謂消耗,常量保證傳入的實例不會被改變。
- 避免內(nèi)存泄漏。如果忘記在分配新內(nèi)存前釋放自身已有空間,程序?qū)⒊霈F(xiàn)內(nèi)存泄露。
- 判斷是否是自身。判斷傳入的實例和自身是否是同一個實例,如果是,那就直接返回該實例,否則在釋放實例自身已有空間后就無法再找到需要賦值的內(nèi)容了。
考慮異常安全的解法:
- 如果內(nèi)存不足,導(dǎo)致new char拋出異常,m_pdata將是一個空指針,容易導(dǎo)致程序崩潰,也就是說一旦在賦值運算符函數(shù)內(nèi)部拋出一個異常,String的實例被修改,不再保持有效狀態(tài),這就違背了異常安全性原則。
- 有兩種方法:第一種是先用new分配新內(nèi)容再delete釋放已有內(nèi)容。這樣只有new成功后再釋放原來的內(nèi)容,也就是說失敗時能保證String的實例不會被修改。更好的方法是先創(chuàng)建一個臨時實例,再交換臨時實例和原來的實例。
String& String::operator=(const String&str)
{
if(this!=&str)
{
String strTemp(str);
char* pTemp=strTemp.m_pdata;
strTemp.m_pdata=m_pdata;
m_pdata=pTemp;
}
return *this;
}
該代碼中,在String的構(gòu)造函數(shù)里用new分配內(nèi)存。如果由于內(nèi)存不足拋出異常,還沒有修改原來實例的狀態(tài),因此實例的狀態(tài)還是有效的,保證了異常安全性。