中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

微網(wǎng)站開發(fā)平臺免費網(wǎng)絡推廣公司介紹

微網(wǎng)站開發(fā)平臺免費,網(wǎng)絡推廣公司介紹,厘米售卡站怎么做網(wǎng)站,海外分銷平臺🌈個人主頁:秋風起,再歸來~🔥系列專欄:C從入門到起飛 🔖克心守己,律己則安 目錄 1、多文件之間的關系 2、模擬實現(xiàn)常用的構(gòu)造函數(shù) 2.1 無參構(gòu)造函數(shù) 2.2 有參的構(gòu)造函數(shù) 2.3 析構(gòu)函…

🌈個人主頁:秋風起,再歸來~
🔥系列專欄:C++從入門到起飛? ? ? ? ??
🔖克心守己,律己則安

目錄

1、多文件之間的關系

2、模擬實現(xiàn)常用的構(gòu)造函數(shù)

2.1 無參構(gòu)造函數(shù)

2.2 有參的構(gòu)造函數(shù)

?2.3 析構(gòu)函數(shù)(順便實現(xiàn))

3、size()、capacity()、[]運算符重載

4、模擬實現(xiàn)簡單的正向迭代器

5、reserve、push_back、append

6、operator+=、insert、erase

7、find、substr

8、非成員函數(shù)operator比較系列

9、非成員函數(shù)operator<<、operator>>

10. 完結(jié)散花


1、多文件之間的關系

>string.h

在string.h中我們用來包含各種頭文件以及定義我們的string類和非成員函數(shù)的聲明

注意:在string.h中string類的定義非成員函數(shù)的聲明放到我們自己定義的命名空間my_string中(原因就是為了和庫里面的std:string類進行區(qū)分!)

>string.cpp

在string.cpp中我們來完成string類中一些(類里面短小頻繁調(diào)用的函數(shù)聲明和定義不用分離)成員函數(shù)和非成員函數(shù)的定義!

注意:在string.cpp中我們要包含“string.h”并且要在命名空間域中完成成員函數(shù)和非成員函數(shù)的定義!

>test.cpp

這個文件用來測試我們的接口是否有bug!

2、模擬實現(xiàn)常用的構(gòu)造函數(shù)

這里我們實現(xiàn)的是簡單的string類,沒有搞vs下用buff數(shù)組來存放字符串那一套,所以我們就只有三個成員變量:

private:char* _str;//指向字符串的指針size_t _size;//有效字符個數(shù)(不包含'\0')size_t _capacity;//空間大小(不包含'\0')

注意:這里的有效字符個數(shù)_size和空間大小_capacity都不包含'\0'!但我們實際開空間時都會多開一個來存放’\0‘?

2.1 無參構(gòu)造函數(shù)

聲明:以下實現(xiàn)的函數(shù)都是直接在類里面定義(短小頻繁調(diào)用,在類里面直接默認為inline)

>有誤的無參構(gòu)造函數(shù)

	string():_str(nullptr)//有問題,標準庫里面的是可以輸出空字符串的, _size (0),_capacity (0)
{}

在寫構(gòu)造函數(shù)時,一般我們都是建議顯示寫初始化列表的,于是我們上來可能就會寫出如上的代碼!不過上面的代碼并不符合C++標準規(guī)定,當我們空參構(gòu)造時,庫里面輸出的是一個空字符串,而上面寫的構(gòu)造函數(shù)卻是不能直接訪問的空指針!

在測試代碼前,因為我們還沒有重載流提取和流插入函數(shù),那我們就先實現(xiàn)一個簡單的c_str()函數(shù)來幫助我們實現(xiàn)打印輸出!

>返回C字符串(c_str())

//返回C字符串
const char* c_str() const
{return _str;
}

>指定命名空間使用庫里面的string

std::string s1;//指定命名空間用的是庫里面的string
cout << s1.c_str() << endl;

通過調(diào)試我們發(fā)現(xiàn),庫里面的string空參構(gòu)造一個string對象s1時, s1里面存放的是一個’\0‘,并輸出一個空字符串!

>未指定命名空間,在命名空間my_string內(nèi),優(yōu)先使用自己實現(xiàn)的string

//未指定命名空間,在命名空間my_string內(nèi),優(yōu)先使用自己實現(xiàn)的string
string s1;
cout << s1.c_str() << endl;

?通過調(diào)試我們發(fā)現(xiàn),我們模擬實現(xiàn)的string空參構(gòu)造一個string對象s1時, s1里面存放的是一個nullptr,所以我們在輸出時,程序就直接崩潰了!

>正確的無參構(gòu)造函數(shù)?

那我們就按照標準庫的規(guī)定來寫,在走初始化列表時開一個空間來存放’\0‘即可!

string():_str(new char[1]{'\0'})//實際的空間大小要比capacity大1來存放'\0', _size (0),_capacity (0)
{}

2.2 有參的構(gòu)造函數(shù)

走初始化列表通過計算str的長度來開辟空間并確定_size和_capacity的大小,然后在函數(shù)體內(nèi)將str的值拷貝到_str中,我們就完成了帶參數(shù)的構(gòu)造函數(shù)!

string(const char* str):_str(new char[strlen(str) + 1]), _size(strlen(str)),_capacity(strlen(str))
{strcpy(_str, str);
}

?不過這里并不建議走初始化列表,因為每次都要調(diào)用strlen(),有性能和效率的消耗。

建議寫下面這一種版本!

string(const char* str)//加上缺省值合二為一
{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//記住開空間時多開一個strcpy(_str, str);//會把str的'\0'拷貝進來
}

當然,我們還可以將無參構(gòu)造函數(shù)和帶參構(gòu)造函數(shù)合二為一!?

string(const char* str="")//加上缺省值合二為一
{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//記住開空間時多開一個strcpy(_str, str);//會把str的'\0'拷貝進來
}

?注意:合二為一之后我們就要將之前寫的無參構(gòu)造函數(shù)屏蔽掉,不然編譯器不知道調(diào)用誰就會報錯!(一個類中只能有一個默認構(gòu)造函數(shù)(即可以無參調(diào)用的構(gòu)造函數(shù))!)

?2.3 析構(gòu)函數(shù)(順便實現(xiàn))

因為有資源的申請,所以我們要顯示實現(xiàn)我們的析構(gòu)函數(shù)!

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

3、size()、capacity()、[]運算符重載

聲明:以下實現(xiàn)的函數(shù)都是直接在類里面定義(短小頻繁調(diào)用,在類里面直接默認為inline)

這些接口都比較簡單我就不贅述了

//返回size和capacity
size_t size() const
{return _size;
}
size_t capacity() const
{return _capacity;
}
//[]運算符重載
char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}
//const版本
const char& operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}

好啦!到這里,我們配合一下前面實現(xiàn)的一些接口,測試一下有沒有什么問題!?

string s1("hello world");
for (size_t i = 0; i < s1.size(); i++)
{cout << s1[i];
}
cout << endl;
for (size_t i = 0; i < s1.size(); i++)
{s1[i] += 2;cout << s1[i];
}

好,這里看到結(jié)果也是沒有任何問題的呢!

4、模擬實現(xiàn)簡單的正向迭代器

聲明:以下實現(xiàn)的函數(shù)都是直接在類里面定義(短小頻繁調(diào)用,在類里面直接默認為inline)

上面用下標訪問遍歷了我們的string對象,范圍for這么方便,那我們也來嘗試用它來遍歷一下吧!

string s1("hello world");
for (auto ch :s1 )
{cout << ch;
}

完蛋了!一寫出來就給我們報了一大堆的錯誤!

不過,我在上一篇博客里寫到了范圍for的底層就是迭代器,我們冷靜下來思考并結(jié)合報錯就會發(fā)現(xiàn)原來我們自己實現(xiàn)的string類中目前還沒有迭代器,所以我們不能用范圍for來遍歷s1

那我們怎么來實現(xiàn)string類的迭代器呢?

這里我就直接告訴大家,所有的迭代器iterator都是typedef出來的!在這里迭代器其實就是典型的封裝的一種體現(xiàn),所有的容器(鏈表,隊列,樹等)都有迭代器,并且使用他們的迭代器的方式都是一樣的(即迭代器給我們提供了統(tǒng)一的接口,但其底層的實現(xiàn)并不相同,不過我們在使用時并不關心它底層的細節(jié),我們只要掌握了迭代器的使用方式,就會對所有容器進行操作。這種封裝的方式大大方便了我們對容器的使用!)

好啦!到這里我們就來實現(xiàn)一下string類里面簡單的一個迭代器吧!

我們上篇文章就說過在string中,正向迭代器的使用就可以把它當做指針來看(不一定是指針),那我們在實現(xiàn)時不就可以參考使用原始指針的方式來實現(xiàn)我們的簡單迭代器呢?

1. 我們將char*重新命名為iterator(即iterator就是char* 的類型)

2. 然后我們再實現(xiàn)begin()和end()倆個接口來返回_str的開頭和結(jié)尾

typedef char* iterator;
//正向迭代器
iterator begin()const 
{return _str;
}
iterator end() const
{return _str+_size;
}

好啦!到這里我們就實現(xiàn)好了一個簡單的正向迭代器!我們來測試一下:

	string s1("hello world");for (auto ch :s1 ){cout << ch;}cout<< endl;

?我們再簡單的實現(xiàn)一下正向常量迭代器!

//正向常量迭代器
typedef const char* const_iterator;
const_iterator cbegin() const
{return _str;
}
const_iterator cend() const
{return _str + _size;
}

?這里,我們就不實現(xiàn)反向迭代器了(用原始指針已經(jīng)解決不了了),因為它要用到一個叫適配器的東西(目前我也不知道那是啥玩意),還挺復雜的~

5、reserve、push_back、append

>reserve

標準庫里面的reserve只有在預留空間大于容量時才會擴容并且決不改變有效字符個數(shù),strcpy會拷貝到\0!

	void string::reserve(size_t n){//標準庫里面的reserve只有在預留空間大于容量時才會擴容//并且決不改變有效字符if (n > _capacity){char* tmp = new char[n + 1];//開新空間,記得加一strcpy(tmp, _str);//拷貝數(shù)據(jù)到新空間delete[] _str;//釋放舊空間_str = tmp;//_str指向新空間_capacity = n;}return;}

??好啦!寫到這里,我們來測試一下上面的接口是否有問題!

string s1("hello world");
cout << "capacity:" << s1.capacity() << endl;
//reserve
s1.reserve(20);
cout << "capacity:" <<s1.capacity() << endl << endl;

string s1("hello world");
cout << "capacity:" << s1.capacity() << endl;
//reserve
s1.reserve(10);
cout << "capacity:" <<s1.capacity() << endl << endl;

好啦, 也是沒有任何問題的!

>push_back

先判斷是否需要擴容,空間為0,則開4個空間,否則二倍擴,記得要手動放一個'\0'!

void string::push_back(char c)
{//先判斷是否需要擴容if (_size == _capacity){	//空間為0,則開4個空間,否則二倍擴!reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size++] = c;//有效字符已經(jīng)更新_str[_size] = '\0';//記得要手動放一個'\0'
}

??好啦!寫到這里,我們來測試一下上面的接口是否有問題!?

string s1("hello world");
cout << s1.c_str() << endl;
s1.push_back('x');
cout << s1.c_str() << endl<<endl;

?OK啊!也是沒有任何問題的!

>append?

先判斷是否需要擴容,原字符串與追加的字符串總長度大于2 * _capacity,就開len + _size,否則二倍擴,不要忘了更新_size,strcpy會拷貝到\0!

	string& string::append(const char* s){size_t len = strlen(s);if (len + _size > _capacity){//原字符串與追加的字符串總長度大于2 * _capacity,就開len + _size,否則二倍擴reserve(len + _size > 2 * _capacity ? len + _size : 2 * _capacity);}strcpy(_str + _size, s);_size += len;//不要忘了更新_sizereturn *this;}

?好啦!寫到這里,我們來測試一下上面的接口是否有問題!??

string s1("hello world");
cout << s1.c_str() << endl;
s1.append("test append ");
cout << s1.c_str() << endl << endl;

6、operator+=、insert、erase

>operator+=

直接復用push_back即可!

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

因為是復用的代碼,就不測試了!?

>insert(任意位置前插入一個字符)

//任意位置前插入一個字符
string& string::insert(size_t pos, char c)
{assert(pos <=_size);//先判斷是否需要擴容if (_size == _capacity){	//空間為0,則開4個空間,否則二倍擴!reserve(_capacity == 0 ? 4 : _capacity * 2);}for (size_t i = _size; i >=pos; i--){//把pos位置開始 的字符全部后移一個位置_str[i + 1] = _str[i];}_str[pos] = c;_size++;return *this;
}

?好啦!寫到這里,我們來測試一下上面的接口是否有問題!

我們先尾插一個字符!

string s1("hello world");
cout << s1.insert(s1.size(), '*').c_str() << endl << endl;

沒有什么問題!

我們再頭插一個字符!

string s1("hello world");
cout << s1.insert(0, '&').c_str() << endl << endl;

?我的發(fā)!壞了,程序直接崩潰了。我們程序員最害怕的就是自己寫的程序測試出bug來,不過我們不要慌,我們調(diào)試一下來解決問題!

按照我們的挪動邏輯,循環(huán)結(jié)束前后i的位置應該如下!?

我們來調(diào)試檢查檢查一下哪里出了問題!

通過調(diào)試我們發(fā)現(xiàn)頭插前 i 的值雀氏為11

所有的數(shù)據(jù)都按我們的想法挪 動到后面去了,不過!i的值卻不是-1,而是一個非常大的數(shù)值!

好了,這里我們就大概明白哪里出問題了,這里其實就是C語言遺留下來的一個坑,i的類型是size_t是無符號的整型,當i的值為-1時,其在內(nèi)存中的補碼是全一的序列,而它又是無符號的整型(正數(shù)),因此這全一的序列會被認為是該值的原碼?(即這是整型的最大值!)

解決這個問題的方法有很多,比如可以用int來解決,不過我這里就直接改變一下挪動的邏輯啦!

for (size_t i = _size+1; i >pos; i--)
{//把pos位置開始 的字符全部后移一個位置_str[i] = _str[i-1];
}

這樣挪i就不會到-1?

>insert(任意位置前插入一個字符串)

注釋很詳細啦,友友們認真看哦~

//任意位置前插入一個字符串
string& string::insert(size_t pos, const char* s)
{assert(pos <=_size);size_t len = strlen(s);if (len + _size > _capacity){//原字符串與追加的字符串總長度大于2 * _capacity,就開len + _size,否則二倍擴reserve(len + _size > 2 * _capacity ? len + _size : 2 * _capacity);}//把pos位置開始 的字符全部后移len個位置//1、用庫函數(shù)memmove一個一個字節(jié)的拷貝挪動(注意一定要多挪動一個字節(jié),把'\0'也挪動到后面去)memmove(_str + pos + len, _str + pos, (len+1) * sizeof(char));//2、手動挪/*for (size_t i = _size + len; i > pos+len-1; i--){_str[i] = _str[i - len];}*///一個一個字符拷貝for (size_t i = 0; i <  len ; i++){_str[pos+i] =s[i];}_size += len;return *this;
}

>erase?

string& erase(size_t pos, size_t len=npos);

上面的代碼是類里面的成員函數(shù)聲明有缺省值npos(這是在類里面聲明的一個靜態(tài)的常量成員)

static const size_t npos;

注意:

//static const size_t npos=-1;
//特殊的可以在聲明(類內(nèi)部)處定義的static成員變量
//而且只有整形可以
//static const double d = 1.1;報錯:const double類型不能包含類內(nèi)初始值設定項?

不過,我們這里還是建議讓靜態(tài)成員變量定義到類外中,不過,我們要注意定義一定不要在頭文件中,不然我們在test.cpp和string.cpp中包含了倆次npos的定義,這時在鏈接時編譯器就找不到重復定義的成員從而發(fā)生鏈接錯誤!

所以我們在string.cpp中定義npos!

聲明處給了缺省值,定義處就不能顯示寫缺省值了!先判斷pos的有效性,再判斷從pos位置開始的字符夠不夠刪,如果不夠,直接在pos位置放\0,并更新有效字符的個數(shù)為pos。如果夠刪,就走挪動的邏輯!

從任意位置開始刪除len個字符string& string::erase(size_t pos, size_t len){assert(pos < _size);if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{//把pos+len位置開始的字符全部前移len個位置//1、用庫函數(shù)memmove一個一個字節(jié)的挪動(注意一定要多挪動一個字節(jié),把'\0'也挪動到后前                //面去)//memmove(_str + pos, _str +pos+ len, (_size -pos+1) * sizeof(char));//2、手動挪for (size_t i = pos + len; i <=_size; i++){_str[i-len] = _str[i];}_size -= len;}return *this;}

7、find、substr

>find(從pos位置開始找字符c)

循環(huán)遍歷查找即可,沒什么好說的,但要注意如果沒有找到返回npos

//從pos位置開始找字符c
size_t string::find(char c, size_t pos )
{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == c){return i;}}return npos;
}

?>find從(pos位置開始找字符串s)

找子串問題,可以用kmp算法,但該算法在實際應用中用的并不多(C語言中strstr用的就不是該算法),而BM算法就用的較多,不過BM算法較為復雜,有興趣的小伙伴可以自己去查閱一下資料哦!我們下面就直接使用庫里面的函數(shù)來匹配子串了!????????

//從pos位置開始找字符串s
size_t string::find(const char* s, size_t pos )
{assert(pos < _size);char* ret=strstr(_str + pos, s);if (ret == nullptr){return npos;}return ret - _str;
}

>substr(從pos位置開始取子串)

//從pos位置開始取子串
string string::substr(size_t pos, size_t len)
{if (len > _size-pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = pos; i < _size; i++){sub += _str[i];}return sub;
}

測試代碼:?

string s1("hello world");
string s2 = s1.substr(6);
cout << s2.c_str() << endl;

這里我就直接說結(jié)論,我們上面的代碼還是有問題的,我們在vs2022debug版本上測試倒不會出現(xiàn)什么問題,但vs2019或更早一點的版本就會有運行時錯誤。因為我還沒有安裝更早的版本,這里就沒辦法演示了。

那到底是哪里出了問題呢?我們在函數(shù)里面創(chuàng)建了一個局部變量sub來暫時存放取到的子串,并傳值返回,在傳值返回時,函數(shù)還會先調(diào)用拷貝構(gòu)造來拷貝一個臨時的string對象,然后再用臨時的string對象來拷貝構(gòu)造我們在函數(shù)外面用來接收返回值的string對象s2

?不過,編譯器默認的拷貝構(gòu)造是淺拷貝,即將對象一個一個字節(jié)的拷貝構(gòu)造另一個對象

通過調(diào)試我們發(fā)現(xiàn)s2的_str和sub的_str指向的是同一塊空間!當sub出函數(shù)的局部作用域時,sub對象就會調(diào)用析構(gòu)函數(shù)而銷毀,而其所指向的空間也同時被釋放!?

所以,當一個類里面有成員向內(nèi)存申請資源時,我們就不能使用編譯器默認生成的拷貝構(gòu)造了,必須自己顯示生成拷貝構(gòu)造進行深拷貝

//拷貝構(gòu)造(深拷貝)
string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}

那為什么在vs2022debug版本上測試倒不會出現(xiàn)什么問題呢?這里簡單的說一下,在進行傳值拷貝構(gòu)造時,編譯器可能不會進行sub對象的創(chuàng)建,直接用臨時對象拷貝構(gòu)造s2,而在這里,編譯器的優(yōu)化更為激進直接和三為一,sub和臨時對象都不創(chuàng)建了,直接拷貝構(gòu)造s2!所以也就不存在同一塊空間被多次析構(gòu)的問題了!

8、非成員函數(shù)operator比較系列

這個系列比較簡單,大家看看代碼就明白了!

bool operator<(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) < 0;
}
bool operator<=(const string& s1, const string& s2)
{return s1 < s2 || s1 == s2;
}
bool operator>(const string& s1, const string& s2)
{return !(s1 <= s2);
}
bool operator>=(const string& s1, const string& s2)
{return !(s1 < s2);
}
bool operator==(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) == 0;
}
bool operator!=(const string& s1, const string& s2)
{return !(s1 == s2);
}

9、非成員函數(shù)operator<<、operator>>

> operator<<

ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;
}

> operator>>

istream& operator>>(istream& in, string& s)
{s.clear();//先清理有效字符char ch;in >> ch;//將流里面的字符插入到ch中while (ch != ' ' && ch != '\n'){s += ch;in >> ch;}s += '\0';//末尾記得加上'\0'return in;
}

上面的代碼看似沒問題,其實這樣寫in讀取不到緩沖區(qū)里面的換行和空格,與scanf一樣,cin在讀取字符時,默認將空格和換行視為字符分割符不進行讀取(記住就行)!所以我們就可以用in.get()來讀取每一個字符,作用和getc一樣!

istream& operator>>(istream& in, string& s)
{s.clear();//先清理有效字符char ch;ch = in.get();//將流里面的字符插入到ch中while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}s += '\0';//末尾記得加上'\0'return in;
}

如果字符串很長,會有頻繁的擴容消耗,可以優(yōu)化一下

istream& operator>>(istream& in, string& s)
{s.clear();//先清理有效字符const size_t N = 256;int i = 0;char buff[N];//用一個buff數(shù)組做我們的緩沖char ch= in.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1)//數(shù)組滿了再把字符加進s中,避免頻繁擴容{buff[i] = '\0';i = 0;s += buff;}ch = in.get();}if (i > 0)//把最后一組沒有滿的也加上{buff[i] = '\0';s += buff;}return in;
}

10、完整代碼

>string.h

#pragma once#include<iostream>
#include<assert.h>
using namespace std;namespace my_string
{class string{public://類里面短小頻繁調(diào)用的函數(shù)聲明和定義不用分離//1、無參構(gòu)造函數(shù)//string()//	:_str(new char[1]{'\0'})//實際的空間大小要比capacity大1來存放'\0'//	, _size (0)//	,_capacity (0)//{}//	string()//	:_str(nullptr)//有問題,標準庫里面的是可以輸出空字符串的//	, _size (0)//	,_capacity (0)//{}//2、有參的構(gòu)造函數(shù)string(const char* str="")//加上缺省值合二為一{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//記住開空間時多開一個strcpy(_str, str);//會把str的'\0'拷貝進來}/** 不建議走初始化列表,每次都要調(diào)用strlen()!string(const char* str):_str(new char[strlen(str) + 1]), _size(strlen(str)),_capacity(strlen(str)){strcpy(_str, str);}*///3、析構(gòu)函數(shù)~string(){delete[] _str;_str = nullptr;_capacity = _size = 0;}//4、返回C字符串const char* c_str() const{return _str;}//5、返回size和capacitysize_t size() const{return _size;}size_t capacity() const{return _capacity;}//6、[]運算符重載char& operator[](size_t pos){assert(pos < _size);return _str[pos];}//const版本const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}//7、實現(xiàn)兩個簡單的迭代器typedef char* iterator;//正向迭代器iterator begin()const {return _str;}iterator end() const{return _str+_size;}//正向常量迭代器typedef const char* const_iterator;const_iterator cbegin() const{return _str;}const_iterator cend() const{return _str + _size;}//拷貝構(gòu)造(深拷貝)string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}//顯示賦值運算符重載string& operator=(const string& s){if (this != &s){delete[] _str;_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this;}void clear(){_str[0] = '\0';_size = 0;}//下面的成員函數(shù)聲明和定義分離//0、預留空間void reserve(size_t n);//1、尾插一個字符void push_back(char c);//2、追加一個字符串string& append(const char* s);//3、+=一個字符string& operator+=(char s);//4、+=一個字符串string& operator+=(const char* s);//5、任意位置插入一個字符string& insert(size_t pos, char c);//6、任意位置插入一個字符串string& insert(size_t pos,const char* s);//7、從任意位置刪除len個字符string& erase(size_t pos, size_t len=npos);//8、從pos位置開始找字符csize_t find(char c, size_t pos = 0);//9、從pos位置開始找字符串ssize_t find(const char* s, size_t pos = 0);//10、從pos位置開始取子串string substr(size_t pos = 0, size_t len = npos);private:char* _str;//指向字符串的指針size_t _size;//有效字符個數(shù)(不包含'\0')size_t _capacity;//空間大小(不包含'\0')static const size_t npos;//static const size_t npos=-1;//特殊的可以在聲明(類內(nèi)部)處定義的static成員變量//而且只有整形可以//static const double d = 1.1;報錯:const double類型不能包含類內(nèi)初始值設定項friend ostream& operator<<(ostream& out, const string& s);};//const size_t string::npos = -1;//非成員函數(shù)!bool operator<(const string& s1, const string& s2);bool operator<=(const string& s1, const string& s2);bool operator>(const string& s1, const string& s2);bool operator>=(const string& s1, const string& s2);bool operator==(const string& s1, const string& s2);bool operator!=(const string& s1, const string& s2);ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& in, string& s);
}

>string.cpp

#define _CRT_SECURE_NO_WARNINGS#include"string.h"namespace my_string
{const size_t string::npos = -1;void string::reserve(size_t n){//標準庫里面的reserve只有在預留空間大于容量時才會擴容//并且決不改變有效字符if (n > _capacity){char* tmp = new char[n + 1];//開新空間,記得加一strcpy(tmp, _str);//拷貝數(shù)據(jù)到新空間delete[] _str;//釋放舊空間_str = tmp;//_str指向新空間_capacity = n;}return;}void string::push_back(char c){//先判斷是否需要擴容if (_size == _capacity){	//空間為0,則開4個空間,否則二倍擴!reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size++] = c;_str[_size] = '\0';//記得要手動放一個'\0'}string& string::append(const char* s){size_t len = strlen(s);if (len + _size > _capacity){//原字符串與追加的字符串總長度大于2 * _capacity,就開len + _size,否則二倍擴reserve(len + _size > 2 * _capacity ? len + _size : 2 * _capacity);}strcpy(_str + _size, s);_size += len;//不要忘了更新_sizereturn *this;}string& string::operator+=(char c){push_back(c);return *this;}string& string::operator+=(const char* s){append(s);return *this;}//5、任意位置前插入一個字符string& string::insert(size_t pos, char c){assert(pos <=_size);//先判斷是否需要擴容if (_size == _capacity){	//空間為0,則開4個空間,否則二倍擴!reserve(_capacity == 0 ? 4 : _capacity * 2);}//for (size_t i = _size; i >=pos; i--)//{//	//把pos位置開始 的字符全部后移一個位置//	_str[i + 1] = _str[i];//	//這種寫法有bug//}for (size_t i = _size+1; i >pos; i--){//把pos位置開始 的字符全部后移一個位置_str[i] = _str[i-1];}_str[pos] = c;_size++;return *this;}//6、任意位置前插入一個字符串string& string::insert(size_t pos, const char* s){assert(pos <=_size);size_t len = strlen(s);if (len + _size > _capacity){//原字符串與追加的字符串總長度大于2 * _capacity,就開len + _size,否則二倍擴reserve(len + _size > 2 * _capacity ? len + _size : 2 * _capacity);}//把pos位置開始 的字符全部后移len個位置//1、用庫函數(shù)memmove一個一個字節(jié)的拷貝挪動(注意一定要多挪動一個字節(jié),把'\0'也挪動到后面去)memmove(_str + pos + len, _str + pos, (len+1) * sizeof(char));//2、手動挪/*for (size_t i = _size + len; i > pos+len-1; i--){_str[i] = _str[i - len];}*/for (size_t i = 0; i <  len ; i++){_str[pos+i] =s[i];}_size += len;return *this;}//7、從任意位置開始刪除len個字符string& string::erase(size_t pos, size_t len){assert(pos < _size);if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{//把pos+len位置開始的字符全部前移len個位置//1、用庫函數(shù)memmove一個一個字節(jié)的挪動(注意一定要多挪動一個字節(jié),把'\0'也挪動到后前面去)//memmove(_str + pos, _str +pos+ len, (_size -pos+1) * sizeof(char));//2、手動挪for (size_t i = pos + len; i <=_size; i++){_str[i-len] = _str[i];}_size -= len;}return *this;}//8、從pos位置開始找字符csize_t string::find(char c, size_t pos ){assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == c){return i;}}return npos;}//9、從pos位置開始找字符串ssize_t string::find(const char* s, size_t pos ){assert(pos < _size);char* ret=strstr(_str + pos, s);if (ret == nullptr){return npos;}return ret - _str;}//10、從pos位置開始取子串string string::substr(size_t pos, size_t len){if (len > _size-pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = pos; i < _size; i++){sub += _str[i];}//注意一定要自己實現(xiàn)拷貝構(gòu)造,sub是局部的return sub;}bool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator<=(const string& s1, const string& s2){return s1 < s2 || s1 == s2;}bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}bool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}//>1、這樣寫有問題!讀取不到換行和'\0'//istream& operator>>(istream& in, string& s)//{//	s.clear();//先清理有效字符//	char ch;//	in >> ch;//將流里面的字符插入到ch中//	while (ch != ' ' && ch != '\n')//	{//		s += ch;//		in >> ch;//	}//	s += '\0';//末尾記得加上'\0'//	return in;//}//>2、如果字符串很長,會有頻繁的擴容消耗,可以優(yōu)化一下//istream& operator>>(istream& in, string& s)//{//	s.clear();//先清理有效字符//	char ch;//	ch = in.get();//將流里面的字符插入到ch中//	while (ch != ' ' && ch != '\n')//	{//		s += ch;//		ch = in.get();//	}//	s += '\0';//末尾記得加上'\0'//	return in;//}istream& operator>>(istream& in, string& s){s.clear();//先清理有效字符const size_t N = 256;int i = 0;char buff[N];//用一個buff數(shù)組做我們的緩沖char ch= in.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1)//數(shù)組滿了再把字符加進s中,避免頻繁擴容{buff[i] = '\0';i = 0;s += buff;}ch = in.get();}if (i > 0)//把最后一組沒有滿的也加上{buff[i] = '\0';s += buff;}return in;}
}

>test.cpp


#include"string.h"
using namespace my_string;
namespace my_string
{void test_my_string1(){string s1;cout << s1.c_str() << endl;/*string s2("hello world");cout << s2.c_str() << endl;*/}void test_my_string2(){/*	string s1("hello world");for (size_t i = 0; i < s1.size(); i++){cout << s1[i];}cout << endl;for (size_t i = 0; i < s1.size(); i++){s1[i] += 2;cout << s1[i];}cout << endl;*///string::const_iterator it = s1.cbegin();//while (it != s1.cend())//{//	//*it += 2;報錯:表達式必須是可修改的左值//	cout << *it;//	it++;//}//cout << endl;string s1("hello world");for (auto ch :s1 ){cout << ch;}cout<< endl;}void test_my_string3(){//string s1("hello world");//cout << "capacity:" << s1.capacity() << endl;reserve//s1.reserve(10);//cout << "capacity:" <<s1.capacity() << endl << endl;//push_back/*string s1("hello world");cout << s1.c_str() << endl;s1.push_back('x');cout << s1.c_str() << endl<<endl;*/+=//cout << s1.c_str() << endl;//s1 += '&';//cout << s1.c_str() << endl << endl;//append/*string s1("hello world");cout << s1.c_str() << endl;s1.append("test append ");cout << s1.c_str() << endl << endl;*///append//cout << s1.c_str() << endl;//s1+="test+= ";//cout << s1.c_str() << endl << endl;}void test_my_string4() {//Test insert/*string s1("hello world");cout << s1.insert(s1.size(), '*').c_str() << endl << endl;*/string s1("hello world");cout << s1.insert(0, '&').c_str() << endl << endl;//cout << s1.insert(s1.size(), "test insert s").c_str() << endl << endl;/*string s2("hello world");s2.erase(6,2);cout << s2.c_str()<<endl;s2.erase(0,8);cout << s2.c_str() << endl;*/}void test_my_string5(){//Test findstring s1("hello world");/*size_t ret = s1.find("llo");cout << ret << endl;*/string s2 = s1.substr(6);cout << s2.c_str() << endl;}void test_my_string6(){//Test << >>string s1="hello world";cout << s1 << endl;cin >> s1;cout << s1;}
}int main()
{test_my_string5();
}

11. 完結(jié)散花

好了,這期的分享到這里就結(jié)束了~

如果這篇博客對你有幫助的話,可以用你們的小手指點一個免費的贊并收藏起來喲~

如果期待博主下期內(nèi)容的話,可以點點關注,避免找不到我了呢~

我們下期不見不散~~

????

http://www.risenshineclean.com/news/1045.html

相關文章:

  • 大連電子商務網(wǎng)站建設網(wǎng)絡營銷的真實案例分析
  • wordpress更換網(wǎng)站域名seo技術培訓
  • 對網(wǎng)站備案的認識賬號seo是什么
  • 北京軟件公司有哪些seo任務
  • 做視頻網(wǎng)站 買帶寬谷歌廣告聯(lián)盟一個月能賺多少
  • wordpress站內(nèi)優(yōu)化網(wǎng)絡營銷評價的名詞解釋
  • 嗶哩嗶哩b站肉片免費入口在哪里自己可以創(chuàng)建網(wǎng)站嗎
  • 友情網(wǎng)站制作藝人百度指數(shù)排行榜
  • 烏魯木齊住房和城鄉(xiāng)建設廳網(wǎng)站百度上首頁
  • 在農(nóng)村做相親網(wǎng)站怎么樣百度域名提交收錄網(wǎng)址
  • 網(wǎng)站在其他地區(qū)備案買友情鏈接
  • 最便宜做公司網(wǎng)站app營銷策劃方案
  • 如何進入微網(wǎng)站同城引流用什么軟件
  • 怎樣建設網(wǎng)站是什么意思全網(wǎng)推廣費用
  • 網(wǎng)站發(fā)布初期的推廣seo每天一貼
  • wordpress 編輯器 視頻教程東莞seo優(yōu)化方案
  • 怎樣買網(wǎng)站建設濟南seo網(wǎng)站排名優(yōu)化工具
  • 醫(yī)療美容培訓網(wǎng)站建設搜索引擎培訓班
  • 織夢網(wǎng)站推廣插件無憂軟文網(wǎng)
  • 網(wǎng)站代碼修改某個產(chǎn)品營銷推廣方案
  • 自己的網(wǎng)站如何做快照劫持搜索引擎外部優(yōu)化有哪些渠道
  • wordpress登錄安全插件下載網(wǎng)站優(yōu)化策劃書
  • 網(wǎng)站建設編輯部搜索網(wǎng)站的瀏覽器
  • 工業(yè)軟件開發(fā)技術就業(yè)前景seo代做
  • 體育類網(wǎng)站 設計百度下載2022新版安裝
  • 外貿(mào)電子商務網(wǎng)站建設軟件外包公司排行
  • 天津網(wǎng)站開發(fā)公司 智善美科技網(wǎng)絡廣告營銷策略
  • google提交網(wǎng)站入口關鍵詞推廣優(yōu)化外包
  • h5游戲中心seo優(yōu)化需要多少錢
  • 昆明seo公司網(wǎng)站不用流量的地圖導航軟件