詳解C++中String類(lèi)模擬實(shí)現(xiàn)以及深拷貝淺拷貝
詳解C++中String類(lèi)模擬實(shí)現(xiàn)以及深拷貝淺拷貝
在C語(yǔ)言中/C++中,字符串是一個(gè)應(yīng)用很廣泛的類(lèi)型,也是很基礎(chǔ)的類(lèi)型,C語(yǔ)言并沒(méi)有直接處理字符串的操作而是采用字符指針和字符串?dāng)?shù)組進(jìn)行操作,而在C++中標(biāo)準(zhǔn)庫(kù)為我們封裝了一個(gè)字符串的類(lèi)供我們使用,使用需要#inlcude <string>頭文件。我們也可以自己模擬實(shí)現(xiàn)一個(gè)簡(jiǎn)單的String類(lèi)。
在模擬實(shí)現(xiàn)String類(lèi)的過(guò)程中,不可避免的會(huì)遇到深拷貝淺拷貝的問(wèn)題,下面就深拷貝淺拷貝做一個(gè)簡(jiǎn)介。所謂深拷貝淺拷貝,簡(jiǎn)單來(lái)說(shuō)就是淺拷貝只是簡(jiǎn)單的將值拷貝過(guò)來(lái),用一個(gè)對(duì)象初始化另一個(gè)對(duì)象,只復(fù)制了成員,并沒(méi)有復(fù)制資源,使兩個(gè)對(duì)象同時(shí)指向了同一資源的。而深拷貝則是將資源和值一塊拷貝過(guò)來(lái),此時(shí)兩個(gè)對(duì)象各自占用資源,盡管值相同,但是互不影響。
下面通過(guò)代碼進(jìn)行對(duì)比:
//淺拷貝
class String {
public:
String(const char* s = "")
{
if (NULL == s) {
_pStr = new char[1];
*_pStr = '\0';
}
else {
_pStr = new char[strlen(s) + 1];
strcpy(_pStr, s);
}
}
String(const String& s)
{
_pStr = s._pStr;
}
String& operator=(const String& s)
{
if (this != &s) {
_pStr = s._pStr;
}
return *this;
}
~String()
{
if (NULL != _pStr) {
delete[] _pStr;
_pStr = NULL;
}
}
private:
char* _pStr;
};
//深拷貝
class String {
public:
String(const char* s = "")
{
if (NULL == s) {
_pStr = new char[1];
*_pStr = '\0';
}
else {
_pStr = new char[strlen(s) + 1];
strcpy(_pStr, s);
}
}
String(const String& s) : _pStr(new char[strlen(s._pStr) + 1])
{
strcpy(_pStr, s._pStr);
}
String& operator=(const String& s)
{
if (this != &s) { //先申請(qǐng)空間將s的內(nèi)容拷貝到一個(gè)臨時(shí)變量再去釋放原有的空間
char* temp = new char[strlen(s._pStr) + 1];//防止申請(qǐng)空間失敗連原有的空間都沒(méi)了
strcpy(temp, s._pStr);
delete[] _pStr;
_pStr = NULL;
_pStr = temp;
}
return *this;
}
~String()
{
if (NULL != _pStr) {
delete[] _pStr;
_pStr = NULL;
}
}
private:
char* _pStr;
};


由圖可以看出,淺拷貝使得多個(gè)對(duì)象指向一塊空間,然而當(dāng)最后析構(gòu)的時(shí)候,比如c先釋放空間,而a,b卻不知道還要釋放空間便會(huì)產(chǎn)生重復(fù)釋放同一內(nèi)存的錯(cuò)誤。為此,我們可以對(duì)淺拷貝進(jìn)行一個(gè)優(yōu)化,例如在私有成員中加入一個(gè)int*
pCount來(lái)標(biāo)記一塊空間被幾個(gè)對(duì)象占用,當(dāng)只有一個(gè)對(duì)象占用如果進(jìn)行析構(gòu)便可釋放空間,否則只對(duì)*pCount--。
//淺拷貝優(yōu)化--帶有計(jì)數(shù)版本的String類(lèi),用指針指向計(jì)數(shù)的空間
class String {
public:
String(const char* s = "") : _pCount(new int(1))
{
if (NULL == s) {
_pStr = new char[1];
*_pStr = '\0';
}
else {
_pStr = new char[strlen(s) + 1];
strcpy(_pStr, s);
}
}
String(const String& s)
{
_pStr = s._pStr;
_pCount = s._pCount;
(*_pCount)++;
}
String& operator=(const String& s)
{
if (this != &s) {
if (--(*_pCount) == 0) {
delete[] _pStr;
delete _pCount;
}
_pStr = s._pStr;
_pCount = s._pCount;
(*_pCount)++;
}
return *this;
}
~String()
{
if (NULL != _pStr && --(*_pCount) == 0) {
delete[] _pStr;
delete _pCount;
}
_pCount = NULL;
_pStr = NULL;
}
private:
char* _pStr;
int* _pCount;
};
最后再給出一種深拷貝的簡(jiǎn)潔版本,通過(guò)調(diào)用swap來(lái)簡(jiǎn)化操作,代碼如下:
//深拷貝的簡(jiǎn)潔寫(xiě)法
class String {
public:
String(const char* s = "")
{
if (NULL == s) {
_pStr = new char[1];
*_pStr = '\0';
}
else {
_pStr = new char[strlen(s) + 1];
strcpy(_pStr, s);
}
}
String(String& s) :_pStr(NULL)//必須對(duì)_pStr初始化,防止釋放隨機(jī)值的空間
{
String temp(s._pStr);
swap(_pStr, temp._pStr);
}
String& operator=(String& s)
{
if (this != &s) {
swap(_pStr, s._pStr);
}
return *this;
}
~String()
{
if (NULL != _pStr) {
delete[] _pStr;
_pStr = NULL;
}
}
private:
char* _pStr;
};
如有疑問(wèn)請(qǐng)留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
C++利用inotify+epoll實(shí)現(xiàn)異步文件監(jiān)控的方法
這篇文章講給大家詳細(xì)介紹一下C++利用inotify+epoll實(shí)現(xiàn)異步文件監(jiān)控的方法,inotify是一種異步文件監(jiān)控機(jī)制,文章通過(guò)代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2023-08-08
C語(yǔ)言函數(shù)調(diào)用底層實(shí)現(xiàn)原理分析
這篇文章主要介紹了C語(yǔ)言函數(shù)調(diào)用底層實(shí)現(xiàn)原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
判斷指定的進(jìn)程或程序是否存在方法小結(jié)(vc等)
VC判斷進(jìn)程是否存在?比如我想知道記事本是否運(yùn)行,要用到哪些函數(shù)等實(shí)例,需要的朋友可以參考下2013-01-01
C語(yǔ)言文件操作零基礎(chǔ)新手入門(mén)保姆級(jí)教程
在實(shí)際應(yīng)用中,我們往往需要對(duì)文件進(jìn)行操作,下面這篇文章主要給大家分享了關(guān)于C語(yǔ)言文件操作的零基礎(chǔ)新手入門(mén)保姆級(jí)教程,文中通過(guò)示例代碼以及圖片介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10

