目錄
一、深淺拷貝
二、String類的定義
三、string類的構造函數
四、string的拷貝構造
1.傳統寫法
2.現代寫法
1)定義一個string類定制的swap函數
2)定義我們基于交換的拷貝構造
測試代碼
五、string類的疊代器
測試代碼
六、對string類中私有類成員的擷取
測試代碼
七、像字元串數組一檔讀取string類中的元素
測試代碼
八、指派等于=
1.傳統寫法
2.現代寫法
3.終極寫法
九、string的擴容函數
1.reserve
2.resize
測試代碼
十、追加函數的實作
1.push_back
2.append
1)追加一個字元串
2)追加一個string
3)追加n個相同的字元ch
3.加等操作
1)使用+=追加一個字元
2)使用+=追加一個字元串
測試代碼
十一、在指定位置插入insert
1.在指定位置插入一個字元
2.在指定位置插入一個字元串
測試程式
十二、擦除指定位置的資料erase
測試程式
十三、查找指定的字元
1)find一個字元
2)find一個字元串
測試程式
3)提取指定位置pos開始的n個字元substr
測試程式
十四、布爾判等操作
測試程式
十五、流插入和流提取操作
1.流插入
優化
2.流提取
優化
測試程式
十六、主程式合集
十七、測試程式合集
1.定義在主程式中的調用測試程式
2.測試程式
首先分各個子產品列出string類的模拟實作,最後會有統一的彙總代碼
一、深淺拷貝
淺拷貝:也稱位拷貝,編譯器隻是将對象中的值拷貝過來。如果對象中管理資源,最後就會導緻多個對象共享同一份資源,當一個對象銷毀時就會将該資源釋放掉,而此時另一些對象不知道該資源已經被釋放,以為還有效,是以當繼續對資源進項操作時,就會發生發生了通路違規。
可以采用深拷貝解決淺拷貝問題,即:每個對象都有一份獨立的資源,不要和其他對象共享。父母給每個孩子都買一份玩具,各自玩各自的就不會有問題了。
二、String類的定義
為了防止調用庫中的string,我們自定義了燭淵的命名空間
#pragma once
#include <iostream>
#include <assert.h>
namespace zhuyuan
{
class string
{
public:
//析構函數的定義
~string()
{
//分别将字元串中的字元串數組銷毀,并且将指向字元串數組的指針置空
delete[] _str;
_str= nullptr;
//這裡的_size指的是目前存儲的字元個數
//這裡的_capacity指的是開辟的空間最大存儲的空間大小
_size=0;
_capacity=0;
}
private:
char* _str;
size_t _size;
size_t _capacity;
//c++的特例,const靜态可以在類裡面直接聲明和定義。
//文法特殊處理,直接可以當成初始化。
public:
const static size_t npos=-1;
};
//當然也可以放在類外面初始化
// size_t string::npos=-1;
一個size_t的大小是8 ,64位平台下的指針大小為8個位元組,一個我們寫的string的大小就是24個位元組
但是實際上我們的編譯器可能會給我們開辟一個類似于緩沖池之類的結構讓我們的資料到了一定的數量才拷貝到我們的string中。
三、string類的構造函數
由于目前我們的String類還沒有顯式定義其拷貝構造函數與指派運算符重載,此時編譯器會合成預設的,當用s1構造s2時,編譯器會調用預設的拷貝構造。最終導緻的問題是,s1、s2共用同一塊記憶體空間,在釋放時同一塊空間被釋放多次而引起程式崩潰,這種拷貝方式,稱為淺拷貝。
在“四”中有我們的顯式拷貝構造函數,寫了“四”中的函數就不會報錯了
string(const char* str="")
{
_size=strlen(str);
_capacity=_size;
_str=new char[_capacity+1];
strcpy(_str,str);
}
string s1("hello world");
string s2(s1);
s2需要調用string類的拷貝構造函數來建立,但是該類沒有顯式定義,則使用系統合成的預設拷貝構造函數,Test函數結束時,需要将s1和s2銷毀掉。先銷毀s2,s2将其__str所指向的空間釋放掉,s2對象成功銷毀,但是s1中_str成為野指針,當銷毀s1時出錯。
當然也可以使用初始化清單的形式,但是需要注意的是,在我們下面這個版本的寫法中,_size
聲明得需要比_capacity更早一些,但是初始化清單的定義的順序不是按照初始化清單中的順序來的,而是按照類中聲明和定義的順序來的
string(const char* str = "")
: _size(strlen(str))
, _capacity(_size)
, _str(new char[_capacity + 1])
{
strcpy(_str, str);
}
四、string的拷貝構造
1.傳統寫法
傳統寫法就是定義一個初始化清單,将我們傳入的要拷貝的string類中的參數一一複制。
//s2(s1)
//傳統寫法
string (const string& s)
:_str(new char[s._capacity+1])
,_size(s._size)
,_capacity(s._capacity)
{
strcpy(_str,s._str);
}
2.現代寫法
1)定義一個string類定制的swap函數
//現代寫法--老闆思維
//s2(s1)
//這裡寫的是針對于string類型的swap,直接交換内部的成員變量
void swap(string &tmp)
{
//::表示調用的是全局的swap函數,跟我們上面那個swap不是同一個。
::swap(_str,tmp._str);
::swap(_size,tmp._size);
::swap(_capacity,tmp._capacity);
}
2)定義我們基于交換的拷貝構造
這裡我們的現代寫法就是老闆式思維,也就是要剝削員工。這裡我們就是調用構造函數。因為我們的string tmp(s._str)也就是調用了我們上面“三”中的構造函數,是以會将我們傳入的s._str作為新的string中的字元串值生成一個新的string,然後拷貝給tmp,而我們所需要做的就是将我們的tmp和我們的this進行交換。但是由于我們原來的this所指向的對象并沒有初始化,是以交換給tmp的是一堆随機值,在tmp調用析構函數的時候,這些随機值會導緻程式崩潰,是以我們需要給this所指向的對象使用初始化清單初始化一下。
string (const string& s)
//如果下面将随機值換給tmp,然後tmp在調用析構函數的時候會崩潰的。
:_str(nullptr)
,_size(0)
,_capacity(0)
{
//調用構造函數
string tmp(s._str);
//本質是this->swap(tmp)
swap(tmp);
}
測試代碼
void test_string3()
{
string s1("hello world");
string s2(s1);
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
s2[0] = 'x';
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
string s3("111111111111111111111111111111");
s1 = s3;
cout << s1.c_str() << endl;
cout << s3.c_str() << endl;
s1 = s1;
cout << s1.c_str() << endl;
cout << s3.c_str() << endl;
}
void test_string4()
{
string s1("hello world");
string s2("xxxxxxx");
s1.swap(s2);
cout<<s1.c_str()<<endl;
cout<<s2.c_str()<<endl;
swap(s1, s2);
cout<<s1.c_str()<<endl;
cout<<s2.c_str()<<endl;
}
五、string類的疊代器
對于string類來說疊代器就像是指針一樣,是以我們可以采用下面這種寫法。我們是定義了一個char*類型的指針iterator和常量指針const_iterator。
begin()就是傳回字元串數組的首元素的位址位置。
end()就是傳回字元串數組尾元素的位址位置,隻要首元素再加上數組的元素的個數就可以了。
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;
}
測試代碼
void test_string2()
{
string s1("hello world");
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
it = s1.begin();
while (it != s1.end())
{
*it += 1;
++it;
}
cout << endl;
for (auto ch : s1)
{
cout << ch << " ";
}
cout << endl;
}
六、對string類中私有類成員的擷取
c_str()就是将字元串中的字元串數組傳回,也就是我們c語言中的字元串數組
size()就是将我們字元串數組中的目前存儲的元素個數傳回。
capacity就是将我們字元串數組中的最大開辟的元素的個數傳回。
const char*c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
測試代碼
void test_string5()
{
string s1("hello");
cout << s1.c_str() << endl;
s1.push_back('x');
cout << s1.c_str() << endl;
cout << s1.capacity() << endl;
}
七、像字元串數組一檔讀取string類中的元素
首先我們需要判斷這個傳入的下标索引是否越界,如果越界,就直接assert斷言抛出。
同時我們提供const類型和非const類型的兩個版本。
char& operator[](size_t pos)
{
assert(pos<_size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos<_size);
return _str[pos];
}
測試代碼
void test_string1()
{
/*std::string s1("hello world");
std::string s2;*/
string s1("hello world");
string s2;
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
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]++;
}
for (size_t i = 0; i < s1.size(); ++i)
{
cout << s1[i] << " ";
}
cout << endl;
}
八、指派等于=
1.傳統寫法
對于指派等于,我們需要将被指派的string類中原來的_str字元串析構掉。同時将新的string中的參數拷貝給我們舊的string。
//s1=s3
string &operator=(const string&s)
{
//防止自己跟自己指派
if(this !=&s)
{
//要多開一個給\0
//如果開辟空間失敗會直接抛異常
char *tmp=new char[s._capacity+1];
strcpy(tmp,s._str);
delete[] _str;
_str=tmp;
_size=s._size;
_capacity=s._capacity;
}
return *this;
}
2.現代寫法
現代寫法同樣也是我們的老闆式寫法。也就是拷貝構造生成一個新的string類,然後将這個string類和我們的this指針指向的string對象中的參數進行交換。
//s1=s3
//operator指派的現代寫法
string& operator=(const string& s)
{
if(this !=&s)
{
//這兩個都可以,因為我們之前的構造和拷貝構造都寫了。
//string tmp(s._str);
string tmp(s);
//不能使用swap(s1,s2),因為庫函數中的swap會調用=,
//也就是我們現在正在寫的這個函數
//跟這個代碼形成循環拷貝,形成棧溢出
swap(tmp);//this->swap(tmp)
}
return *this;
}
3.終極寫法
//s1=s3
//s1頂替tmp做打勞工
//之前我們operator=右邊寫的參數都是const string& s
//但是現在我們寫的參數是string s,也就是說我們并不是引用s3
//而是編譯器會幫助我們構造一個新的s3,然後再用拷貝swap交換給我們的s1。
string& operator=(string s)
{
// this->swap(s);
swap(s);
return *this;
}
九、string的擴容函數
1.reserve
reverse所需要做的就是開辟一塊新的字元串數組,然後将我們舊的字元串數組中的資料拷貝到新的字元串中,然後析構舊的字元串數組。同時更新_capacity參數。
void reserve(size_t n)
{
if(n>_capacity)
{
//開辟一塊新的空間
//+1是為了留給\0
char *tmp=new char[n+1];
//将原來的空間中的資料拷貝到新的
strcpy(tmp,_str);
delete[] _str;
_str=tmp;
_capacity=n;
}
}
2.resize
void resize(size_t n ,char ch='\0')
{
//如果大于了我們目前的_size就擴容,如果小了就删資料
if(n>_size)
{
//插入資料
reserve(n);
for(size_t i=_size;i<n;++i)
{
_str[i]=ch;
}
_str[n]='\0';
_size=n;
}
else
{
//删除資料
_str[n]='\0';
_size=n;
}
}
測試代碼
void test12()
{
string s1;
s1.resize(20);
cout<<s1.size()<<endl;
string s2("hello");
s2.resize(20,'x');
cout<<s2.size()<<endl;
}
十、追加函數的實作
1.push_back
push_back就是追加一個字元,然後再追加之前需要判斷是不是滿了,滿了就要擴容,同時調用我們的reserve函數開辟一塊新的空間。最後不要忘記要加上\0。
void push_back(char ch)
{
//滿了就擴容
if(_size==_capacity)
{
reserve(_capacity==0 ? 4:_capacity*2);
}
_str[_size]=ch;
++_size;
_str[_size]='\0';
}
2.append
append所追加的是一整個字元串,或者是一個string。
1)追加一個字元串
//append是添加一個字元串
void append(const char* str)
{
//len是需要加入的字元串的長度
size_t len=strlen(str);
//滿了就擴容
if(_size+len>_capacity)
{
reserve(_size+len);
}
//初始位置再加上我們原有的長度就是我們目标的拷貝位置。
strcpy(_str+_size,str);
// strcat沒有strcpy好,因為strcat需要先找到'\0'才會追加,
// 但是周遊查找'\0'的方式速度太慢了,不如strcpy直接指定追加的位置。
// strcat(_str,str);
_size+=len;
}
2)追加一個string
void append(const string &s)
{
append(s._str);
}
3)追加n個相同的字元ch
void append(size_t n ,char ch)
{
reserve(_size+n);
for(size_t i=0;i<n;++i)
{
push_back(ch);
}
}
3.加等操作
1)使用+=追加一個字元
一般我們都是使用+=而不使用append的。
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
2)使用+=追加一個字元串
string& operator+=(const char* str)
{
append(str);
return *this;
}
測試代碼
void test_string5()
{
string s1("hello");
cout << s1.c_str() << endl;
s1.push_back('x');
cout << s1.c_str() << endl;
cout << s1.capacity() << endl;
s1 += 'y';
s1 += 'z';
s1 += 'z';
s1 += 'z';
s1 += 'z';
s1 += 'z';
s1 += 'z';
cout << s1.c_str() << endl;
cout << s1.capacity() << endl;
}
十一、在指定位置插入insert
1.在指定位置插入一個字元
string& insert(size_t pos,char ch)
{
assert(pos<_size);
//滿了就擴容
if(_size==_capacity)
{
reserve(_capacity==0 ? 4:_capacity*2);
}
//
// size_t end=_size;
//這樣的話會出問題,因為end要小于pos才會結束,而如果pos等于0的話,我們的end就要是-1才能結束
//而我們的pos由于是end由于是size_t類型也就是無符号整型,是沒辦法表示負數的,是以會陷入死循環!
//
// while(end>=(int)pos)
// {
// _str[end+1]=_str[end];
// --end;
// }
// _str[pos]=ch;
// ++_size;
//可以将end相比于上面+1,然後将跳出循環條件改成>來杜絕上面那種死循環的情況
size_t end=_size+1;
while(end>pos)
{
_str[end]=_str[end-1];
--end;
}
_str[pos]=ch;
++_size;
return *this;
}
2.在指定位置插入一個字元串
//在指定位置插入一整個字元串
string& insert(size_t pos,const char* ch)
{
assert(pos<=_size);
size_t len= strlen(ch);
if(_size+len>_capacity)
{
reserve(_size+len);
}
//挪動資料
size_t end=_size+len;
while(end>=pos+len)
{
_str[end]=_str[end-len];
--end;
}
strncpy(_str+pos,ch,len);
_size+=len;
return *this;
}
測試程式
void test_string6()
{
string s1("hello");
cout << s1.c_str() << endl;
s1 += ' ';
s1.append("world");
s1 += "bit hello";
cout << s1.c_str() << endl;
s1.insert(5,'1');
cout << s1.c_str() << endl;
s1.insert(0, '2');
cout << s1.c_str() << endl;
}
void test_string7()
{
string s1("hello");
cout << s1.c_str() << endl;
s1.insert(2, "world");
cout << s1.c_str() << endl;
s1.insert(0, "world ");
cout << s1.c_str() << endl;
}
十二、擦除指定位置的資料erase
erase是擦除指定位置pos的len個資料。如果我們pos位置越界了就直接斷言派出異常,如果我們的len越界或者是len+pos月季的話就就在pos的位置截斷
其他正常情況的話就将要删除部分後面的資料拷貝到前面來覆寫掉,然後更新我們的_size資料。
void erase(size_t pos,size_t len=npos)
{
assert(pos<_size);
if(len==npos||pos+len>=_size)
{
_str[pos]='\0';
_size=pos;
}
else
{
strcpy(_str+pos,_str+pos+len);
_size-=len;
}
}
測試程式
void test_string8()
{
string s1("hello");
s1.erase(1, 10);
cout << s1.c_str() << endl;
string s2("hello");
s2.erase(1);
cout << s2.c_str() << endl;
string s3("hello");
s3.erase(1, 2);
cout << s3.c_str() << endl;
}
十三、查找指定的字元
1)find一個字元
查找從指定位置開始比對得上的第一個字元
size_t find(char ch,size_t pos=0)
{
assert(pos<_size);
for (int i=pos;i<_size;i++)
{
if (_str[i]==ch)
{
return i;
}
}
}
2)find一個字元串
從指定位置查找第一個比對的上的字元串。使用strstr可以很友善地完成。
size_t find(const char*str,size_t pos=0)
{
assert(pos<_size);
//strstr如果查找成功會傳回對應的起始位置的位址
const char*ret=strstr(_str+pos,str);
//如果查找到了這個位址,就将位址相減,得出相對距離
if(ret)
{
return (ret-_str);
}
else
{
return npos;
}
}
測試程式
string s16="made in China";
cout<<s16.find("China",3)<<endl;
cout<<s16.find('i',2)<<endl;
3)提取指定位置pos開始的n個字元substr
//"ko no dio da"
string substr(size_t pos,size_t len=npos) const
{
assert(pos< _size);
size_t reallen=len;
if(len==npos||pos+len>_size)
{
reallen =_size-pos;
}
string sub;
for(size_t i=0;i<reallen;++i)
{
sub+= _str[pos+i];
}
return sub;
}
測試程式
void test11()
{
string s19="ko no dio da";
cout<<s19.substr(4,10)<<endl;
}
十四、布爾判等操作
對于字元串之間的比較可以調用strcmp來輔助完成。
bool operator==(const string&s) const
{
return (strcmp(c_str(),s.c_str())==0) ? true: false;
};
bool operator>=(const string&s) const
{
return (strcmp(c_str(),s.c_str())>=0) ? true: false;
};
bool operator<=(const string&s) const
{
return (strcmp(c_str(),s.c_str())<=0) ? true: false;
};
bool operator>(const string&s) const
{
return (strcmp(c_str(),s.c_str())>0) ? true: false;
};
bool operator<(const string&s) const
{
return (strcmp(c_str(),s.c_str())<0) ? true: false;
};
bool operator!=(const string&s) const
{
return (strcmp(c_str(),s.c_str())!=0) ? true: false;
};
//或者是像這種進行複用,注意不要循環複用,一般寫三個就可以用複用表示出所有的
// bool operator>=(const string&s) const
// {
// return *this>s ||*this==s;
// };
測試程式
string s13="konodioda";
string s14="konokimojogada";
string s15="konodioda";
cout<<(s13==s14)<<endl;
cout<<(s13==s15)<<endl;
cout<<(s13>=s14)<<endl;
cout<<(s14>=s13)<<endl;
cout<<(s13<=s14)<<endl;
cout<<(s14<=s13)<<endl;
cout<<(s13>s14)<<endl;
cout<<(s14>s13)<<endl;
cout<<(s13<s14)<<endl;
cout<<(s14<s13)<<endl;
cout<<(s13!=s14)<<endl;
cout<<(s13>s14)<<endl;
cout<<(s13!=s15)<<endl;
十五、流插入和流提取操作
1.流插入
//流插入
ostream &operator<<(ostream& out ,const string&s)
{
for(size_t i=0;i<s.size();++i)
{
//将我們的數組中的元素一個個傳給out,由于緩沖池的存在,速度不會特别慢
out<<s[i];
}
return out;
}
優化
可以先清空一下目前的輸入緩沖區
void clear()
{
_size = 0;
_str[_size] = '\0';
}
//流插入
ostream &operator<<(ostream& out ,const string&s)
{
out.clear();
for(size_t i=0;i<s.size();++i)
{
out<<s[i];
}
return out;
}
2.流提取
//流提取
istream & operator>>(istream& in,string&s)
{
//如果輸入字元串很長,不斷+=需要頻繁擴容,效率很低
char ch;
//cin預設空格或者換行是分隔符,預設忽略,是以無法結束輸入,
//一定要使用get()方法
//in>>ch
ch=in.get();
while(ch!=' '&&ch!='\n')
{
s+=ch;
//cin預設空格或者換行是分隔符,預設忽略,是以要使用get()方法
ch=in.get();
}
return in;
}
優化
為了減少上面頻繁開辟空間的情況,我們可以用一個定長的臨時數組來存儲我們的輸入,也就是充當緩沖區的作用,當填充結束或者超出了臨時定長數組的長度就需要将其拷貝給我們的string對象。
istream & operator>>(istream& in,string&s)
{
//如果輸入字元串很長,不斷+=需要頻繁擴容,效率很低
char ch;
//in>>ch
ch=in.get();
const size_t N=32;
//給\0留一個位置
char buff[N+1];
size_t i=0;
while(ch!=' '&&ch!='\n')
{
buff[i++]=ch;
if(i==N-1)
{
buff[i]='\0';
s+=buff;
i=0;
}
ch=in.get();
}
buff[i]='\0';
s+=buff;
return in;
}
測試程式
void test_string9()
{
/* string s1;
cin >> s1;
cout << s1 << endl;*/
string s1("hello");
cout << s1 << endl;
cout << s1.c_str() << endl;
s1 += '\0';
s1 += "world";
cout << s1 << endl;
cout << s1.c_str() << endl;
string s3, s4;
cin >> s3 >> s4;
cout << s3<<" "<< s4 << endl;
}
void test10()
{
string s18;
cin>>s18;
cout<<s18.c_str()<<endl;
}
十六、主程式合集
由于有些代碼有好幾個版本,是以注釋到隻剩下一個
#pragma once
#include <iostream>
#include <assert.h>
namespace zhuyuan
{
class string
{
public:
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;
}
// string(const char*str)
// :_str(new char[strlen(str)+1])
// //存儲的有效字元個數
// ,_size(strlen(str))
// ,_capacity(strlen(str))
// {
// strcpy(_str,str);
// }
//string(const char* str = "\0")
/*string(const char* str = "")
:_str(new char[strlen(str)+1])
, _size(strlen(str))
, _capacity(strlen(str))
{
strcpy(_str, str);
}*/
// string(const char* str = "")
// : _size(strlen(str))
// , _capacity(_size)
// , _str(new char[_capacity + 1])
// {
// strcpy(_str, str);
// }
string(const char* str="")
{
_size=strlen(str);
_capacity=_size;
_str=new char[_capacity+1];
strcpy(_str,str);
}
~string()
{
delete[] _str;
_str= nullptr;
_size=0;
_capacity=0;
}
const char*c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
char& operator[](size_t pos)
{
assert(pos<_size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos<_size);
return _str[pos];
}
// string (const char* str = "")
// {
構造String類對象時,如果傳遞nullptr指針,可以認為程式非
// if (nullptr == str)
// {
// assert(false);
// return;
// }
// _str = new char[strlen(str) + 1];
// strcpy(_str, str);
// cout<<&_str<<endl;
// cout<<&str<<endl;
// }
//s2(s1)
//傳統寫法
//如果是淺拷貝的話,析構的時候會析構兩次,程式會崩潰
// string (const string& s)
// :_str(new char[s._capacity+1])
// ,_size(s._size)
// ,_capacity(s._capacity)
// {
// strcpy(_str,s._str);
// }
//現代寫法--老闆思維
//s2(s1)
//這裡寫的是針對于string類型的swap,直接交換内部的成員變量
void swap(string &tmp)
{
//::表示調用的是全局的swap函數,跟我們上面那個swap不是同一個。
::swap(_str,tmp._str);
::swap(_size,tmp._size);
::swap(_capacity,tmp._capacity);
}
string (const string& s)
//如果下面将随機值換給tmp,然後tmp在調用析構函數的時候會崩潰的。
:_str(nullptr)
,_size(0)
,_capacity(0)
{
//調用構造函數
string tmp(s._str);
//本質是this->swap(tmp)
swap(tmp);
}
// //s1=s3
// string &operator=(const string&s)
// {
// //防止自己跟自己指派
// if(this !=&s)
// {
// //要多開一個給\0
// //如果開辟空間失敗會直接抛異常
// char *tmp=new char[s._capacity+1];
// strcpy(tmp,s._str);
//
// delete[] _str;
//
// _str=tmp;
// _size=s._size;
// _capacity=s._capacity;
// }
// return *this;
// }
// //s1=s3
// //operator指派的現代寫法
// string& operator=(const string& s)
// {
// if(this !=&s)
// {
// //這兩個都可以,因為我們之前的構造和拷貝構造都寫了。
// //string tmp(s._str);
// string tmp(s);
// //不能使用swap(s1,s2),因為庫函數中的swap會調用=,跟這個代碼形成循環拷貝,形成棧溢出
// swap(tmp);//this->swap(tmp)
// }
// return *this;
// }
//s1=s3
//s1頂替tmp做打勞工
//之前我們operator=右邊寫的參數都是const string& s
//但是現在我們寫的參數是string s,也就是說我們并不是引用s3
//而是編譯器會幫助我們構造一個新的s3,然後再用拷貝swap交換給我們的s1。
string& operator=(string s)
{
// this->swap(s);
swap(s);
return *this;
}
void reserve(size_t n)
{
if(n>_capacity)
{
//開辟一塊新的空間
char *tmp=new char[n+1];
//将原來的空間中的資料拷貝到新的
strcpy(tmp,_str);
delete[] _str;
_str=tmp;
_capacity=n;
}
}
void resize(size_t n ,char ch='\0')
{
//如果大于了我們目前的_size就擴容,如果小了就删資料
if(n>_size)
{
//插入資料
reserve(n);
for(size_t i=_size;i<n;++i)
{
_str[i]=ch;
}
_str[n]='\0';
_size=n;
}
else
{
//删除資料
_str[n]='\0';
_size=n;
}
}
void push_back(char ch)
{
//滿了就擴容
if(_size==_capacity)
{
reserve(_capacity==0 ? 4:_capacity*2);
}
_str[_size]=ch;
++_size;
_str[_size]='\0';
}
//append是添加一個字元串
void append(const char* str)
{
//len是需要加入的字元串的長度
size_t len=strlen(str);
//滿了就擴容
if(_size+len>_capacity)
{
reserve(_size+len);
}
//初始位置再加上我們原有的長度就是我們目标的拷貝位置。
strcpy(_str+_size,str);
// strcat沒有strcpy好,因為strcat需要先找到'\0'才會追加,
// 但是周遊查找'\0'的方式速度太慢了,不如strcpy直接指定追加的位置。
// strcat(_str,str);
_size+=len;
}
void append(const string &s)
{
append(s._str);
}
void append(size_t n ,char ch)
{
reserve(_size+n);
for(size_t i=0;i<n;++i)
{
push_back(ch);
}
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
string& insert(size_t pos,char ch)
{
assert(pos<_size);
//滿了就擴容
if(_size==_capacity)
{
reserve(_capacity==0 ? 4:_capacity*2);
}
//
// size_t end=_size;
// while(end>=(int)pos)
// {
// _str[end+1]=_str[end];
// --end;
// }
// _str[pos]=ch;
// ++_size;
size_t end=_size+1;
while(end>pos)
{
_str[end]=_str[end-1];
--end;
}
_str[pos]=ch;
++_size;
return *this;
}
//在指定位置插入一整個字元串
string& insert(size_t pos,const char* ch)
{
assert(pos<=_size);
size_t len= strlen(ch);
if(_size+len>_capacity)
{
reserve(_size+len);
}
//挪動資料
size_t end=_size+len;
while(end>=pos+len)
{
_str[end]=_str[end-len];
--end;
}
strncpy(_str+pos,ch,len);
_size+=len;
return *this;
}
void erase(size_t pos,size_t len=npos)
{
assert(pos<_size);
if(len==npos||pos+len>=_size)
{
_str[pos]='\0';
_size=pos;
}
else
{
strcpy(_str+pos,_str+pos+len);
_size-=len;
}
}
size_t find(char ch,size_t pos=0)
{
assert(pos<_size);
for (int i=pos;i<_size;i++)
{
if (_str[i]==ch)
{
return i;
}
}
}
size_t find(const char*str,size_t pos=0)
{
assert(pos<_size);
const char*ret=strstr(_str+pos,str);
if(ret)
{
return (ret-_str);
}
else
{
return npos;
}
}
void clear()
{
_size = 0;
_str[_size] = '\0';
}
//"ko no dio da"
string substr(size_t pos,size_t len=npos) const
{
assert(pos< _size);
size_t reallen=len;
if(len==npos||pos+len>_size)
{
reallen =_size-pos;
}
string sub;
for(size_t i=0;i<reallen;++i)
{
sub+= _str[pos+i];
}
return sub;
}
bool operator==(const string&s) const
{
return (strcmp(c_str(),s.c_str())==0) ? true: false;
};
bool operator>=(const string&s) const
{
return (strcmp(c_str(),s.c_str())>=0) ? true: false;
};
// bool operator>=(const string&s) const
// {
// return *this>s ||*this==s;
// };
// bool operator<=(const string&s) const
// {
// return (strcmp(c_str(),s.c_str())<=0) ? true: false;
// };
bool operator<=(const string&s) const
{
return !(*this>s);
};
// bool operator>(const string&s) const
// {
// return (strcmp(c_str(),s.c_str())>0) ? true: false;
// };
bool operator>(const string&s) const
{
return (strcmp(c_str(),s.c_str())>0) ? true: false;
};
bool operator<(const string&s) const
{
return (strcmp(c_str(),s.c_str())<0) ? true: false;
};
bool operator!=(const string&s) const
{
return (strcmp(c_str(),s.c_str())!=0) ? true: false;
};
private:
char* _str;
size_t _size;
size_t _capacity;
//c++的特例,const靜态可以在類裡面直接聲明和定義。
//文法特殊處理,直接可以當成初始化。
public:const static size_t npos=-1;
};
// size_t string::npos=-1;
//流插入
ostream &operator<<(ostream& out ,const string&s)
{
out.clear();
for(size_t i=0;i<s.size();++i)
{
out<<s[i];
}
return out;
}
// istream & operator>>(istream& in,string&s)
// {
// //如果輸入字元串很長,不斷+=需要頻繁擴容,效率很低
// char ch;
// //in>>ch
// ch=in.get();
// while(ch!=' '&&ch!='\n')
// {
// s+=ch;
// //cin預設空格或者換行是分隔符,預設忽略
// ch=in.get();
// }
// return in;
// }
istream & operator>>(istream& in,string&s)
{
//如果輸入字元串很長,不斷+=需要頻繁擴容,效率很低
char ch;
//in>>ch
ch=in.get();
const size_t N=32;
//給\0留一個位置
char buff[N+1];
size_t i=0;
while(ch!=' '&&ch!='\n')
{
buff[i++]=ch;
if(i==N-1)
{
buff[i]='\0';
s+=buff;
i=0;
}
ch=in.get();
}
buff[i]='\0';
s+=buff;
return in;
}
十七、測試程式合集
1.定義在主程式中的調用測試程式
#include <iostream>
using namespace std;
#include <assert.h>
#include "string.h"
int main() {
try
{
zhuyuan::test_string0();
}
catch(std::exception&e)
{
std::cout<<e.what()<<std::endl;
}
return 0;
}
2.測試程式
// void test_string0()
// {
// string s1("hello world");
// string s2(s1);
// std::cout<<s1.c_str()<<std::endl;
// std::cout<<s2.c_str()<<std::endl;
// string::iterator it;
//
// string s3("sikoni hi teyazida");
// s1=s3;
// std::cout<<s1.c_str()<<std::endl;
// std::cout<<s3.c_str()<<std::endl;
// while(it!=s1.end())
// {
// std::cout<<*it<<" ";
// it++;
// }
// std::cout<<std::endl;
// //範圍for
// for(auto ch :s1)
// {
// std::cout<<ch<<" ";
// }
// std::cout<<std::endl;
// string s4="ko no dio ";
// s4.push_back('d');
// s4.push_back('a');
// cout<<s4.c_str()<<endl;
//
// string s5="ko no ki mo";
// s5+='j';
// s5+='o';
// s5+='g';
// s5+='a';
// s5+='d';
// s5+='a';
// cout<<s5.c_str()<<endl;
// string s6="砸 ";
// string s7="瓦魯多!!";
// s6.append("瓦魯多!!");
// cout<<s6.c_str()<<endl;
// s6.append(s7);
// cout<<s6.c_str()<<endl;
//
// string s5="ko no ki mo";
// s5+="jo ga da";
// cout<<s5.c_str()<<endl;
//
// string s7("helo");
// s7.insert(3,'l');
// cout<<s7.c_str()<<endl;
//
// string s8("what up");
// s8.insert(4,"'s");
// cout<<s8.c_str()<<endl;
//
// string s9("ji ni tai mei");
// s9.erase(3,4);
// cout<<s9.c_str()<<endl;
//
// string s10("三點幾嘞,飲茶先啦!");
// std::cout<<s10<<endl;
//
// string s11;
// cin>>s11;
// cout<<s11;
//
// string s12("hewlloworld");
// //查找從第三個位置後面的w的位置。
// cout<<s12.find('w',3)<<endl;
//
// string s13="konodioda";
// string s14="konokimojogada";
// string s15="konodioda";
// cout<<(s13==s14)<<endl;
// cout<<(s13==s15)<<endl;
// cout<<(s13>=s14)<<endl;
// cout<<(s14>=s13)<<endl;
// cout<<(s13<=s14)<<endl;
// cout<<(s14<=s13)<<endl;
// cout<<(s13>s14)<<endl;
// cout<<(s14>s13)<<endl;
// cout<<(s13<s14)<<endl;
// cout<<(s14<s13)<<endl;
// cout<<(s13!=s14)<<endl;
// cout<<(s13>s14)<<endl;
// cout<<(s13!=s15)<<endl;
//
// string s16="made in China";
// cout<<s16.find("China",3)<<endl;
// cout<<s16.find('i',2)<<endl;
//
//
// }
// void test_string1()
// {
// /*std::string s1("hello world");
// std::string s2;*/
// string s1("hello world");
// string s2;
//
// cout << s1.c_str() << endl;
// cout << s2.c_str() << endl;
//
// 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]++;
// }
//
// for (size_t i = 0; i < s1.size(); ++i)
// {
// cout << s1[i] << " ";
// }
// cout << endl;
// }
void test_string2()
{
string s1("hello world");
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
it = s1.begin();
while (it != s1.end())
{
*it += 1;
++it;
}
cout << endl;
for (auto ch : s1)
{
cout << ch << " ";
}
cout << endl;
}
void test_string3()
{
string s1("hello world");
string s2(s1);
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
s2[0] = 'x';
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
string s3("111111111111111111111111111111");
s1 = s3;
cout << s1.c_str() << endl;
cout << s3.c_str() << endl;
s1 = s1;
cout << s1.c_str() << endl;
cout << s3.c_str() << endl;
}
void test_string4()
{
string s1("hello world");
string s2("xxxxxxx");
s1.swap(s2);
cout<<s1.c_str()<<endl;
cout<<s2.c_str()<<endl;
swap(s1, s2);
cout<<s1.c_str()<<endl;
cout<<s2.c_str()<<endl;
}
void test_string5()
{
string s1("hello");
cout << s1.c_str() << endl;
s1.push_back('x');
cout << s1.c_str() << endl;
cout << s1.capacity() << endl;
s1 += 'y';
s1 += 'z';
s1 += 'z';
s1 += 'z';
s1 += 'z';
s1 += 'z';
s1 += 'z';
cout << s1.c_str() << endl;
cout << s1.capacity() << endl;
}
void test_string6()
{
string s1("hello");
cout << s1.c_str() << endl;
s1 += ' ';
s1.append("world");
s1 += "bit hello";
cout << s1.c_str() << endl;
s1.insert(5,'1');
cout << s1.c_str() << endl;
s1.insert(0, '2');
cout << s1.c_str() << endl;
}
void test_string7()
{
string s1("hello");
cout << s1.c_str() << endl;
s1.insert(2, "world");
cout << s1.c_str() << endl;
s1.insert(0, "world ");
cout << s1.c_str() << endl;
}
void test_string8()
{
string s1("hello");
s1.erase(1, 10);
cout << s1.c_str() << endl;
string s2("hello");
s2.erase(1);
cout << s2.c_str() << endl;
string s3("hello");
s3.erase(1, 2);
cout << s3.c_str() << endl;
}
void test_string9()
{
/* string s1;
cin >> s1;
cout << s1 << endl;*/
string s1("hello");
cout << s1 << endl;
cout << s1.c_str() << endl;
s1 += '\0';
s1 += "world";
cout << s1 << endl;
cout << s1.c_str() << endl;
string s3, s4;
cin >> s3 >> s4;
cout << s3<<" "<< s4 << endl;
}
void test10()
{
string s18;
cin>>s18;
cout<<s18.c_str()<<endl;
}
void test11()
{
string s19="ko no dio da";
cout<<s19.substr(4,10)<<endl;
}
void test12()
{
string s1;
s1.resize(20);
cout<<s1.size()<<endl;
string s2("hello");
s2.resize(20,'x');
cout<<s2.size()<<endl;
}
void test13()
{
std::string s0;
std::string s1("1111");
std::string s2("11111111111111111111111111111111111111111111111111111");
cout<<sizeof(s0)<<sizeof(s1)<<sizeof (s2)<<endl;
cout<<sizeof(size_t)<<endl;
}
}