溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++ STL主要組件之String總結(jié)(第二部分 深、淺拷貝問題以及賦值運算符重載)

發(fā)布時間:2020-08-11 14:38:10 來源:網(wǎng)絡 閱讀:273 作者:sonissa 欄目:編程語言

第一部分連接https://blog.51cto.com/14232799/2447326

二.String的模擬實現(xiàn)

在第一步之后緊接著的就該是模擬實現(xiàn)部分,這一部分主要是體現(xiàn)自己對第一部分的掌握情況。強烈推薦和我一樣在學習String的朋友們自己動手實現(xiàn)一下。因為在面試中,面試官總喜歡讓我們自己來模擬實現(xiàn)string類。

自己來實現(xiàn)String最主要是實現(xiàn)String類的構(gòu)造、拷貝構(gòu)造、賦值運算符重載(第一部分operator開頭的方法)以及析構(gòu)函數(shù)。

以下是我完成的基礎模擬實現(xiàn)

#include<iostream>
#include<assert.h>
using namespace std;

namespace self{
    class string{
    public:
        string(const char* s = " "){
            if (s == nullptr){
                assert(false);
                return;
            }
            _s = new char[strlen(s) + 1];
            strcpy(_s, s);
        }
        ~string(){
            if (_s){
                delete[] _s;
                _s = nullptr;
            }
        }
    private:
        char* _s;
    };
}
int main(){
    self::string k = "hello";
    self::string i("world");
    self::string m;
    //self::string l(k);
    return 0;
}

以上就是沒有重載賦值運算符且沒有顯式定義拷貝構(gòu)造函數(shù)的string類模擬實現(xiàn)。基本完整的模擬實現(xiàn)會在本篇文章的最后給出(當然免不了有紕漏,若是發(fā)現(xiàn)請各位大佬提醒)
上面的代碼中的main函數(shù)中有一句注釋語句 //self::string l(k); 我將其注釋是因為如果加入這一句代碼程序就會運行崩潰!?。。?br/>程序崩潰的原因是: 當我們不去顯式定義拷貝構(gòu)造方法的時候,系統(tǒng)就會生成默認的拷貝構(gòu)造函數(shù),這種拷貝構(gòu)造函數(shù)是一種淺拷貝,最終結(jié)果就是導致 對象l和對象k在共用同一塊內(nèi)存空。看起來似乎沒什么問題?
但是!當函數(shù)結(jié)束時,在調(diào)用析構(gòu)函數(shù)的操作上就會出現(xiàn)大問題。
原本的話,每一個對象都會調(diào)用一次析構(gòu)函數(shù)來清理自己占用的空間。但是當兩個對象所占用的是同一塊空間時,一個對象調(diào)用完析構(gòu)函數(shù)后另一個對象調(diào)用析構(gòu)函數(shù)的時候,就會生同一塊空間被釋放多次的程序錯誤!從而引起程序崩潰!
所以說在以上這個的代碼中不可以使用拷貝構(gòu)造方法。這個問題也就引出了下一個要總結(jié)的部分:淺拷貝和深拷貝

三.淺拷貝和深拷貝

1.淺拷貝
此處只是給個定義:
淺拷貝:也稱位拷貝,編譯器只是將對象中的值拷貝過來。如果對象中管理資源,最后就會導致多個對象共享同一份資源,當一個對象銷毀時就會將該資源釋放掉,而此時另一些對象不知道該資源已經(jīng)被釋放,以為還有效,所以 當繼續(xù)對資源進項操作時,就會發(fā)生發(fā)生了訪問違規(guī)。所以要解決淺拷貝問C++中引入了深拷貝。
(第二部分的string模擬事先就是個例子)
放個圖片占位:
C++ STL主要組件之String總結(jié)(第二部分  深、淺拷貝問題以及賦值運算符重載)
2.深拷貝
“如果一個類中涉及到資源的管理,其拷貝構(gòu)造函數(shù)、賦值運算符重載以及析構(gòu)函數(shù)必須要顯式給出。一般情況都是按照深拷貝方式提供”
上面這句話是真理!
首先給出深拷貝一般在string'類中的實現(xiàn):

String(const String& s)
 : _str(new char[strlen(s._str)+1])   // 看見這一步開辟空間就知道是深拷貝了
 {
 strcpy(_str, s._str);
 }

再來說深拷貝的定義:
每個string都需要空間來存放字符串,而當使用一個string類對象來構(gòu)造另一個string類對象。就用到了深拷貝:給每個對象獨立分配資源,保證多個對象之間不會因共享資源而造成空間多次釋放而造成的程序奔潰問題。

三.String中賦值運算符重載
1.先給出幾種在string中常用的賦值運算符重載:
<1> <<

ostream& bit::operator<<(ostream& _cout, const self::String& s)
{
 cout << s._str;
 return _cout;
}

對于<<的重載算是比較特殊的了,因為會用到ostream類型,所以在這里展開說明一下:
ostream是output stream的簡稱,即輸出流。一個典型的輸出流對象就是在C++中標準輸出流cout。
在C++中,很少自定義ostream的對象,更多的是直接使用cout。
ostream這個類型,往往出現(xiàn)在<<操作重載中,作為某個類的友元函數(shù)出現(xiàn)。
比如對于class A, 可以定義ostream & operator << (ostream &os, const A& a);
這樣在調(diào)用A的對象var時,就可以這樣使用
cout &lt;&lt; var ;

<2> =

String& operator=(String s)
 {
 swap(_str, s._str); 
 return *this;
 }

<3> +=

string& operator+=(char ch)
        {
            push_back(ch);
            return *this;
        }

<4> [ ]

char& operator[](size_t index)
 {
 assert(index < _size);
 return _str[index];
 }

四.最后是給出的較為完整的string類模擬實現(xiàn):

namespace key
{
 class String
 {
 public:
 typedef char* iterator;
 public:
 String(const char* str = "")
 {
 _size = strlen(str);
 _capacity = _size;
 _str = new char[_capacity+1];
 strcpy(_str, str);
 }
 String(const String& s)
 : _str(nullptr)
 , _size(0)
 , _capacity(0)
 {
 String tmp(s);
 this->Swap(tmp);
 }
 String& operator=(String s)
 {
 this->Swap(s)
 return *this;
 }
 ~String()
 {
 if (_str)
 {
 delete[] _str;
 _str = nullptr;
 }
 }
 /////////////////////////////////////////////////////////////////
 // iterator
 iterator begin() {return _str;}
 iterator end(){return _str + _size;}
 /////////////////////////////////////////////////////////////////
 // modify
 void PushBack(char c)
 {
 if (_size == _capacity)
 Reserve(_capacity*2);

 _str[_size++] = c;
 _str[_size] = '\0';
 }
 String& operator+=(char c)
 {
 PushBack(c);
 return *this;
 }
 void Clear()
 {
 _size = 0;
 _str[_size] = '\0';
 }
 void Swap(String& s)
 {
 swap(_str, s._str);
 swap(_size, s._size);
 swap(_capacity, s._capacity);
 }
 const char* C_Str()const
 {
 return _str;
 }
 size_t Size()const
 size_t Capacity()const
 bool Empty()const

 void Resize(size_t newSize, char c = '\0')
 {
 if (newSize > _size)
 {
 // 如果newSize大于底層空間大小,則需要重新開辟空間
 if (newSize > _capacity)
 {
 Reserve(newSize);
 }
 memset(_str + _size, c, newSize - _size);
 }
 _size = newSize;
 _str[newSize] = '\0';
 }
 void Reserve(size_t newCapacity)
 {
 // 如果新容量大于舊容量,則開辟空間
比特科技
 if (newCapacity > _capacity)
 {
 char* str = new char[newCapacity + 1];
 strcpy(str, _str);
 // 釋放原來舊空間,然后使用新空間
 delete[] _str;
 _str = str;
 _capacity = newCapacity;
 }
 }
 char& operator[](size_t index)
 {
 assert(index < _size);
 return _str[index];
 }
 const char& operator[](size_t index)const
 {
 assert(index < _size);
 return _str[index];
 }
 private:
 friend ostream& operator<<(ostream& _cout, const bit::String& s);
 private:
 char* _str;
 size_t _capacity;
 size_t _size;
 };
}
ostream& key::operator<<(ostream& _cout, const bit::String& s)
{
 cout << s._str;
 return _cout;
}
向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI