欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++模擬實(shí)現(xiàn)string的方法詳解

 更新時(shí)間:2022年11月10日 15:49:39   作者:bite-ccc  
標(biāo)準(zhǔn)庫類型string表示可變長的字符序列,使用string類型必須首先包含string的頭文件。本文將利用C++模擬實(shí)現(xiàn)string,需要的可以參考一下

1.string 成員變量

首先需要一個(gè)動(dòng)態(tài)開辟的指針指向這個(gè)字符串,然后還需要容量和存儲(chǔ)的個(gè)數(shù),并且我們不能和標(biāo)準(zhǔn)庫的string進(jìn)行沖突所以我們需要寫在我們自己的類域中,并且我們庫中還有一個(gè)靜態(tài)的變量是npos,就是無符號(hào)的-1,代表整形的最大值:

namespace cyf
{
    class string
    {
    public:
        //成員函數(shù)
    private:
        char *_str;
        size_t size;
        size_t capaticy;
        const static size_t npos = -1;
    };
}

這里有一個(gè)特例:static成員變量一般在類中聲明在類外定義,但是const static int型的變量可以直接在類中定義。

2.構(gòu)造函數(shù)

strlen求出的是\0之前的字符個(gè)數(shù),所以_size和_capacity標(biāo)識(shí)的是實(shí)際存儲(chǔ)的字符個(gè)數(shù),在開辟空間時(shí)多開辟一個(gè)字符用來存儲(chǔ)'\0'。

string(const char* s = "")  
        {
            _size = strlen(s);  
            _capacity = _size;
            _str = new char[_capacity + 1];   
 
            strcpy(_str, s);  //開辟好空間后將s的內(nèi)容拷貝至_str
        }

3.拷貝構(gòu)造、賦值重載和析構(gòu)函數(shù)

1.拷貝構(gòu)造

_str 維護(hù)的是一塊空間,所以不能簡單的將s._str的值賦值給_str (淺拷貝),而是單獨(dú)開辟一塊空間,讓_str指向這一塊空間,再將s._str空間中的值拷貝至新開辟的空間,新開辟的空間比_capacity多開一個(gè)字節(jié)用來存儲(chǔ)'\0',作為字符串的結(jié)束標(biāo)志。

//string s1(s)
string(const string& s)
        {
            _size = s._size;
            _capacity = s._capacity;
            _str = new char[s._capacity + 1];
 
            strcpy(_str, s._str);
        }

2.賦值重載

首先開辟一塊空間,將字符串的內(nèi)容拷貝至這個(gè)空間,將_str原來指向的空間釋放,_str再指向這個(gè)新開辟的空間,size和capacity還是原來的大小。

//s2=s1
        string& operator=(const string& s)
        {
            if (this != &s)   //避免自己給自己賦值
            {
                char* tmp = new char[s._capacity + 1];
                strcpy(tmp, s._str);
                delete[] _str;
                _str = tmp;
                _size = s._size;
                _capacity = s._capacity;
            }
            return *this;
        }

3.析構(gòu)函數(shù)

~string()
        {
            delete[] _str;
            _str = nullptr;
            _size = _capacity = 0;
        }

4.訪問成員變量

提供接口可以在類的外邊查看字符串的內(nèi)容,存儲(chǔ)字符串的元素個(gè)數(shù)和容量

        const char* c_str()
        {
            return _str;
        }
 
        size_t size()
        {
            return _size;
        }
        size_t capacity()
        {
            return _capacity;
        }

配合之前的構(gòu)造函數(shù)和這里的接口,我們進(jìn)行驗(yàn)證:

運(yùn)行結(jié)果:

5.遍歷

遍歷有三種模式:

1.下標(biāo)+【】

_str是一個(gè)指針,那么我們可以通過數(shù)組的方式來訪問,只需要重載operator []即可。我們還是要重載兩個(gè)版本的,因?yàn)槠胀ㄗ兞亢蚦onst變量的訪問權(quán)限不一樣。

//普通變量,可讀可寫
char& operator[](size_t pos)
{
    assert(pos < _size);  //檢查不能越界訪問
 
    return _str[pos];
}
 
//const變量,只讀屬性
char& operator[](size_t pos) const
{
    assert(pos < _size);
 
 
    return _str[pos];
}

2.迭代器(iterator)

在string中,迭代器就是一個(gè)指針,只不過我們進(jìn)行了封裝,typedef一下就可以啦,同樣我們也要實(shí)現(xiàn)兩個(gè)版本的,const和非const的。

typedef char* iterator;
typedef const char* const_iterator;
 
iterator begin()
{
    return _str;
}
 
const_iterator begin()const
{
    return _str;
}
 
iterator end()
{
    return _str +_size;
}
 
const_iterator end() const
{
    return _str +_size;
}

3.范圍for

我們范圍for的底層就是迭代器,所以我們不用實(shí)現(xiàn),只要實(shí)現(xiàn)了迭代器,那么我們就可以直接使用范圍for,范圍for在執(zhí)行的時(shí)候?qū)嶋H還是通過迭代器實(shí)現(xiàn)的,上例子:

運(yùn)行結(jié)果:

6.空間的申請(qǐng)

1.reserve

一般是我們?cè)臻g容量滿了,需要申請(qǐng)空間擴(kuò)容,我們的擴(kuò)容函數(shù)還是要先申請(qǐng)空間,然后在進(jìn)行拷貝,接著我們delete原來的空間,把申請(qǐng)的空間的指針和 容量 賦值過去即可。 

void reserve(size_t n)
        {
            if (n > _capacity)
            {
                char* tmp = new char[n + 1];    //多開一個(gè)字節(jié)留給'\0';
                strcpy(tmp, _str);
                delete[] _str;
                _str = tmp;
 
 
                _capacity = n;
            }
 
 
        }

2.resize

1. 如果我們是傳入一個(gè)正整數(shù)大于_size的值,那么我們可以使用傳入的字符(或者缺省值)把我們申請(qǐng)的空間進(jìn)行初始化,也就是從_size到n-1置為我們傳入的字符,n置為' \0 ',最后把_size置為n。

2.如果傳入一個(gè)小于_size正整數(shù),那么我們把0~_size-1進(jìn)行初始化為傳入的字符(或者缺省值),把n位置置為' \0 ',接著我們會(huì)把_size置為n,而_capaticy不變。

void resize(size_t n, char ch = '\0')
        {
            if (n > _size)
            {
                reserve(n);
                for (size_t i = _size; i < n; ++i)
                {
                    _str[i] = ch;
                }
                _size = n;
                _str[_size] = '\0';
 
            }
 
            else   //小于
            {
                _str[n] = '\0';
                _size = n;
 
            }
 
    }

7.增刪查改

1.push_back   尾插一個(gè)字符

上來就檢查容量,_size==_capacity時(shí)就說明沒有容量了,得分類討論:1.原來的字符串有元素2.原來的是空字符串,如果字符串為空,就給4個(gè)字節(jié)大小的容量 。如果原來的字符串不為空但是需要擴(kuò)容就調(diào)用reserve函數(shù)進(jìn)行擴(kuò)容,容量擴(kuò)為2倍,2倍比較合適,避免給的小了頻繁的擴(kuò)容,但是也不能給的太大了,太大了會(huì)造成空間的浪費(fèi)。在_size的位置插入字符ch ,_size++,插入的字符ch 將原來的'\0'給覆蓋了,最后再補(bǔ)上'\0',作為字符串的結(jié)束標(biāo)志。

void push_back(char ch)
        {
            if (_size == _capacity)
            {
                size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
                reserve(newcapacity);
            }
            _str[_size] = ch;   
            ++(_size);
            _str[_size] = '\0'; //記得處理 \0
        }

2.append 尾部插入一個(gè)字符串

插入字符串的大小不確定,就需要確定是否需要擴(kuò)容。當(dāng)插入的字符串的長度加上當(dāng)前字符串的有效元素個(gè)數(shù)大于容量_capacity時(shí),就需要擴(kuò)容,擴(kuò)后的容量大小為_size+len ,這里給reverse函數(shù)傳的是有效元素的個(gè)數(shù),在reverse函數(shù)內(nèi)部為我們多開了一個(gè)字符的大小用來存儲(chǔ)'\0'。再進(jìn)行拷貝工作,這里記得插入元素后_size要進(jìn)行變換。

void append(const char* str)
        {
            size_t len = strlen(str);
            if (len + _size > _capacity)
            {
                reserve(_size + len);   
 
            }
            strcpy(_str + _size, str);
            _size += len;
        }

3.insert  在指定位置插入一個(gè)字符

上來首先檢查要插入的位置是否合理,_size 代表的位置是有效元素的下一個(gè)位置即'\0'的位置,pos的范圍【0,_size】string字符串的頭到尾部之間 ,插入元素要檢查容量,記得考慮原string是否為空串的情況。插入數(shù)據(jù)需要挪動(dòng)數(shù)據(jù),從前往后挪動(dòng)數(shù)據(jù),將end位置確定到'\0'的下一個(gè)位置,這樣方便頭插。最終將pos位置騰出來,插入字符ch 插入數(shù)據(jù)后_size++。

string& insert(size_t pos, char ch)
        {
            assert(pos <= _size);  //等于size 時(shí)候相當(dāng)于尾部插入
            if (_size == _capacity)
            {
                size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
                reserve(newcapacity);
            }
            size_t end = _size + 1;  //這里是把\0往  \0的下一個(gè)位置挪動(dòng) 方便頭插   
            while (end > pos)
            {
                _str[end] = _str[end - 1];
                --end;
            }
            _str[pos] = ch;
            _size++;
            return *this;
        }

4.insert 在指定位置插入一個(gè)字符串

string& insert(size_t pos, const char* str),我們先進(jìn)行斷言pos不能超過_size,接著我們開辟空間,這次就不考慮空串的問題了,因?yàn)槲覀円付ㄩ_辟的字節(jié)數(shù),和上面一樣的我們也要進(jìn)行挪動(dòng)數(shù)據(jù),我們只不過是由每次挪動(dòng)一個(gè)步,變?yōu)榱伺矂?dòng) len 步了,最后使用strncpy插入字符串,把_szie +=len 即可。

畫圖理解:

string& insert(size_t pos, const char* str)
        {
            size_t len = strlen(str);
            if (_size + len > _capacity)
            {
                reserve(_size + len);
            }
            size_t end = _size + len;
            while (end > pos + len - 1)
            {
                _str[end] = _str[end - len];
                --end;
            }
            strncpy(_str + pos, str, len);
            _size += len;
            return *this;
        }

實(shí)現(xiàn)了上述的接口當(dāng)然最好用的還是下面的接口,對(duì)push_back和append進(jìn)行封裝實(shí)現(xiàn)string+=

一個(gè)字符 和 string+=一個(gè)字符串

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

5.刪除接口:erase

指定字符串從開始位置到指定位置刪除元素。得保證刪除的位置在string的內(nèi)部,當(dāng)只給定了刪除的起始位置沒有給結(jié)束的位置那么就觸法我們的缺省值,即從pos位置開始直到將字符串刪完,或者說給定的結(jié)束位置大于了字符串的本身長度,那就從pos位置開始直到刪除完字符串,實(shí)現(xiàn)的方法很簡單,直接在pos位置添字符串的結(jié)束標(biāo)志'\0'。  如果給定的兩個(gè)值都在字符串的內(nèi)部直接進(jìn)行從len位置往前拷貝覆蓋掉要?jiǎng)h除的元素。

string& erase(size_t pos, size_t len = npos)  
        {
            assert(pos <= len);
            if (len == npos || len >= _size - pos)
            {
                _str[pos] = '\0';
                _size = pos;
            }
            else
            {
                strcpy(_str + pos, _str + pos + len);
                _size -= len;
 
            }
            return *this;
        }

6.find字符 從某個(gè)位置開始查找字符,如果沒有給定開始位置,就用缺省值,默認(rèn)從開頭尋找,找到了就返回元素的下標(biāo),沒有找到就返回npos。

size_t find(char ch, size_t pos = 0)  ///默認(rèn)從pos位置開始尋找,有缺省值0,從pos 位置開始往后尋找
        {                                     //對(duì)比找到了就返回下標(biāo),找不到返回npos
            assert(pos < _size);
            while (pos < _size)
            {
                if (_str[pos] == ch)  //遍歷尋找
                {
                    return pos;
                }
                pos++;
            }
            return npos;
        }

7.find字符串  ,從某個(gè)位置開始往后尋找字符串,找到了就返回下標(biāo),找不到就返回npos

這里套用c語言的庫函數(shù)strstr進(jìn)行實(shí)現(xiàn)

size_t find(const char* str, size_t pos = 0)
        {
            assert(pos < _size);
            const char* ptr = strstr(_str + pos, str);   
            if (ptr == nullptr)  //strstr找不到返回空指針
            {
                return npos;    //轉(zhuǎn)換至cpp找不到就返回npos
            }
            else
            {
                return ptr - _str;  //返回的是下標(biāo)  指針-指針  ==下標(biāo)
            }
 
        }

8.clear  清空字符串的內(nèi)容

直接在第一個(gè)位置加入結(jié)束標(biāo)志就將字符串清空了,將清空后_size就為0,

void clear()
        {
            _size = 0;
            _str[0] = '\0';
        }

8.重載cin 和 cout

1.cout 

依次的輸出string對(duì)象的內(nèi)容即可

    ostream& operator<<(ostream& out,const string& s)
    {
        for (size_t i = 0; i < s.size(); i++)
        {
            out << s[i];
        }
 
        return out;
    }

2.cin

這里注意因?yàn)橐淖冏址膬?nèi)容,首先調(diào)用clear函數(shù)清空原來的內(nèi)容,因?yàn)橐淖冏址膬?nèi)容所以不用const 直接引用改變的就是字符串的本身。因?yàn)槲覀兊?in 會(huì)默認(rèn) '空格' 和 ' \n '是分割符,不進(jìn)行讀取,這樣我們就沒辦法停止。需要使用下 in 的get函數(shù),讓我們的來讀取 ‘  ’  和         ' \n ',我們看下代碼:

void clear()
{
    _str[0] = '\0';
    _size = 0;
}
 
istream& operator>>(istream& in, string& s)
{
    s.clear();//要先進(jìn)行清理,否則就會(huì)出現(xiàn)剩余的數(shù)據(jù)也被我們寫入了。
    char ch;
    ch = in.get();
 
    char buff[32];
    size_t i = 0;
 
    while (ch != ' ' && ch != '\n')
    {
        buff[i++] = ch;
        if (i == 31)
        {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }
        ch = in.get();
    }
 
    buff[i] = '\0';
    s += buff;
 
    return in;
}

cin和cout的重載不一定是類的友元函數(shù),在類中提供接口,我們也可以直接訪問類的成員變量!

到此這篇關(guān)于C++模擬實(shí)現(xiàn)string的方法詳解的文章就介紹到這了,更多相關(guān)C++實(shí)現(xiàn)string內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++使用宏實(shí)現(xiàn)動(dòng)態(tài)庫加載

    C++使用宏實(shí)現(xiàn)動(dòng)態(tài)庫加載

    開發(fā)的時(shí)候,有些項(xiàng)目不能靜態(tài)鏈接動(dòng)態(tài)庫,需要程序運(yùn)行時(shí)加載動(dòng)態(tài)庫。本文將使用宏來實(shí)現(xiàn)動(dòng)態(tài)庫的加載,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-12-12
  • 使用C語言調(diào)用luajit的方法詳解

    使用C語言調(diào)用luajit的方法詳解

    C語言是一種非常流行的編程語言,而Lua是一種基于C語言開發(fā)的腳本語言,在Lua的各種實(shí)現(xiàn)中,luajit也是其中一種非常流行的實(shí)現(xiàn),在本文中,我將為大家介紹如何使用C語言調(diào)用luajit,并且詳細(xì)介紹如何傳入?yún)?shù),傳入結(jié)構(gòu)體參數(shù),以及獲取返回值
    2023-11-11
  • C++實(shí)現(xiàn)合并排序的方法

    C++實(shí)現(xiàn)合并排序的方法

    這篇文章主要介紹了C++實(shí)現(xiàn)合并排序的方法,實(shí)例分析了合并排序的原理與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2015-07-07
  • c++ 讓程序開機(jī)自動(dòng)啟動(dòng)的方法

    c++ 讓程序開機(jī)自動(dòng)啟動(dòng)的方法

    這篇文章主要介紹了c++ 讓程序開機(jī)自動(dòng)啟動(dòng)的方法,需要的朋友可以參考下
    2017-09-09
  • C語言中的setlinebuf()、utmpname()、rewind函數(shù)使用

    C語言中的setlinebuf()、utmpname()、rewind函數(shù)使用

    這篇文章主要介紹了C語言中的setlinebuf()、utmpname()、rewind函數(shù)使用,是C語言中操作文件的一些基本函數(shù),需要的朋友可以參考下
    2015-08-08
  • strtok函數(shù)的使用示例

    strtok函數(shù)的使用示例

    今天小編就為大家分享一篇關(guān)于strtok函數(shù)的使用示例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • C語言實(shí)現(xiàn)三子棋小游戲

    C語言實(shí)現(xiàn)三子棋小游戲

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)三子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • C++17中的折疊表達(dá)式實(shí)現(xiàn)

    C++17中的折疊表達(dá)式實(shí)現(xiàn)

    這篇文章主要介紹了C++17中的折疊表達(dá)式實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • C++ 整數(shù)拆分方法詳解

    C++ 整數(shù)拆分方法詳解

    整數(shù)拆分,指把一個(gè)整數(shù)分解成若干個(gè)整數(shù)的和。本文重點(diǎn)給大家介紹C++ 整數(shù)拆分方法詳解,非常不錯(cuò),感興趣的朋友一起學(xué)習(xí)吧
    2016-08-08
  • c語言實(shí)現(xiàn)的幾種常用排序算法

    c語言實(shí)現(xiàn)的幾種常用排序算法

    C,語言常用的排序方法有很多種。比如說冒泡排序,直接交換排序,直接選擇排序,直接插入排序,二分插入排序,快速排序,歸并排序等等,下面這篇文章主要給大家介紹了關(guān)于c語言實(shí)現(xiàn)幾種常用的排序算法,需要的朋友可以參考下
    2021-06-06

最新評(píng)論