網(wǎng)站首頁 編程語言 正文
1.string 成員變量
首先需要一個動態(tài)開辟的指針指向這個字符串,然后還需要容量和存儲的個數(shù),并且我們不能和標(biāo)準(zhǔn)庫的string進(jìn)行沖突所以我們需要寫在我們自己的類域中,并且我們庫中還有一個靜態(tài)的變量是npos,就是無符號的-1,代表整形的最大值:
namespace cyf
{
class string
{
public:
//成員函數(shù)
private:
char *_str;
size_t size;
size_t capaticy;
const static size_t npos = -1;
};
}
這里有一個特例:static成員變量一般在類中聲明在類外定義,但是const static int型的變量可以直接在類中定義。
2.構(gòu)造函數(shù)
strlen求出的是\0之前的字符個數(shù),所以_size和_capacity標(biāo)識的是實(shí)際存儲的字符個數(shù),在開辟空間時多開辟一個字符用來存儲'\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多開一個字節(jié)用來存儲'\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)容拷貝至這個空間,將_str原來指向的空間釋放,_str再指向這個新開辟的空間,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)容,存儲字符串的元素個數(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是一個指針,那么我們可以通過數(shù)組的方式來訪問,只需要重載operator []即可。我們還是要重載兩個版本的,因?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中,迭代器就是一個指針,只不過我們進(jìn)行了封裝,typedef一下就可以啦,同樣我們也要實(shí)現(xiàn)兩個版本的,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í)行的時候?qū)嶋H還是通過迭代器實(shí)現(xiàn)的,上例子:
運(yùn)行結(jié)果:
6.空間的申請
1.reserve
一般是我們原空間容量滿了,需要申請空間擴(kuò)容,我們的擴(kuò)容函數(shù)還是要先申請空間,然后在進(jìn)行拷貝,接著我們delete原來的空間,把申請的空間的指針和 容量 賦值過去即可。?
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1]; //多開一個字節(jié)留給'\0';
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
2.resize
1. 如果我們是傳入一個正整數(shù)大于_size的值,那么我們可以使用傳入的字符(或者缺省值)把我們申請的空間進(jìn)行初始化,也就是從_size到n-1置為我們傳入的字符,n置為' \0 ',最后把_size置為n。
2.如果傳入一個小于_size正整數(shù),那么我們把0~_size-1進(jìn)行初始化為傳入的字符(或者缺省值),把n位置置為' \0 ',接著我們會把_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 ? 尾插一個字符
上來就檢查容量,_size==_capacity時就說明沒有容量了,得分類討論:1.原來的字符串有元素2.原來的是空字符串,如果字符串為空,就給4個字節(jié)大小的容量 。如果原來的字符串不為空但是需要擴(kuò)容就調(diào)用reserve函數(shù)進(jìn)行擴(kuò)容,容量擴(kuò)為2倍,2倍比較合適,避免給的小了頻繁的擴(kuò)容,但是也不能給的太大了,太大了會造成空間的浪費(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 尾部插入一個字符串
插入字符串的大小不確定,就需要確定是否需要擴(kuò)容。當(dāng)插入的字符串的長度加上當(dāng)前字符串的有效元素個數(shù)大于容量_capacity時,就需要擴(kuò)容,擴(kuò)后的容量大小為_size+len ,這里給reverse函數(shù)傳的是有效元素的個數(shù),在reverse函數(shù)內(nèi)部為我們多開了一個字符的大小用來存儲'\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 ?在指定位置插入一個字符
上來首先檢查要插入的位置是否合理,_size 代表的位置是有效元素的下一個位置即'\0'的位置,pos的范圍【0,_size】string字符串的頭到尾部之間 ,插入元素要檢查容量,記得考慮原string是否為空串的情況。插入數(shù)據(jù)需要挪動數(shù)據(jù),從前往后挪動數(shù)據(jù),將end位置確定到'\0'的下一個位置,這樣方便頭插。最終將pos位置騰出來,插入字符ch 插入數(shù)據(jù)后_size++。
string& insert(size_t pos, char ch)
{
assert(pos <= _size); //等于size 時候相當(dāng)于尾部插入
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size + 1; //這里是把\0往 \0的下一個位置挪動 方便頭插
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
_size++;
return *this;
}
4.insert 在指定位置插入一個字符串
string& insert(size_t pos, const char* str),我們先進(jìn)行斷言pos不能超過_size,接著我們開辟空間,這次就不考慮空串的問題了,因?yàn)槲覀円付ㄩ_辟的字節(jié)數(shù),和上面一樣的我們也要進(jìn)行挪動數(shù)據(jù),我們只不過是由每次挪動一個步,變?yōu)榱伺矂?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)然最好用的還是下面的接口,對push_back和append進(jìn)行封裝實(shí)現(xiàn)string+=
一個字符 和 string+=一個字符串
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'。 ?如果給定的兩個值都在字符串的內(nèi)部直接進(jìn)行從len位置往前拷貝覆蓋掉要刪除的元素。
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字符 從某個位置開始查找字符,如果沒有給定開始位置,就用缺省值,默認(rèn)從開頭尋找,找到了就返回元素的下標(biāo),沒有找到就返回npos。
size_t find(char ch, size_t pos = 0) ///默認(rèn)從pos位置開始尋找,有缺省值0,從pos 位置開始往后尋找
{ //對比找到了就返回下標(biāo),找不到返回npos
assert(pos < _size);
while (pos < _size)
{
if (_str[pos] == ch) //遍歷尋找
{
return pos;
}
pos++;
}
return npos;
}
7.find字符串 ?,從某個位置開始往后尋找字符串,找到了就返回下標(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)容
直接在第一個位置加入結(jié)束標(biāo)志就將字符串清空了,將清空后_size就為0,
void clear()
{
_size = 0;
_str[0] = '\0';
}
8.重載cin 和 cout
1.cout?
依次的輸出string對象的內(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 會默認(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)行清理,否則就會出現(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ù),在類中提供接口,我們也可以直接訪問類的成員變量!
原文鏈接:https://blog.csdn.net/m0_61438674/article/details/127757737
相關(guān)推薦
- 2022-06-22 C++深入探究類與對象之友元與運(yùn)算符重載_C 語言
- 2022-11-05 解決使用pip安裝報(bào)錯:Microsoft?Visual?C++?14.0?is?required.
- 2022-11-24 Linux使用iptables實(shí)現(xiàn)屏蔽ip地址的示例詳解_linux shell
- 2022-11-08 服務(wù)器nginx權(quán)限被拒絕解決案例_nginx
- 2023-02-09 MongoDB中aggregate()方法實(shí)例詳解_MongoDB
- 2022-08-27 Terraform集成簡單Gitlab?CI方案詳解_其它綜合
- 2022-05-04 分享3個非常實(shí)用的?Python?模塊_python
- 2022-07-07 C++?opencv實(shí)現(xiàn)幾何圖形繪制_C 語言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支