1. 序言
????????STL(標(biāo)準模板庫)是一個C++標(biāo)準庫,其中包括一些通用的算法、容器和函數(shù)對象。STL的容器是C++ STL庫的重要組成部分,它們提供了一種方便的方式來管理同類型的對象。其中,STLstring是一種常用的字符串類型。
????????STLstring是一個類,它封裝了字符串的操作,并提供了一組成員函數(shù)。STLstring的實現(xiàn)使用了動態(tài)的內(nèi)存分配技術(shù),這意味著字符串的大小可以隨時改變。STLstring還提供了一些高效的成員函數(shù),例如substr、find、replace等,這些函數(shù)可以對字符串進行快速的操作。
????????STLstring的實現(xiàn)主要基于字符數(shù)組。字符數(shù)組是一種固定大小的數(shù)組,其中每個元素包含一個字符。STLstring使用一個字符數(shù)組來存儲字符串,并通過動態(tài)的內(nèi)存分配技術(shù)來管理數(shù)組的大小。當(dāng)向一個空的STLstring對象中添加字符時,STLstring會自動調(diào)整數(shù)組的大小。
????????STLstring還實現(xiàn)了一些常見的字符串操作,例如連接字符串、查找字符串、替換字符串和分割字符串等。這些操作使用了C++ STL庫中的algorithm算法,可以高效地處理字符串。同時,STLstring也提供了迭代器的支持,允許用戶使用STL算法來處理字符串。
2. string類的接口實現(xiàn)
? ? ? ? 在實現(xiàn)接口之前先要給出我們的初始類,包括三個私有成員(_size,_capacity,_str),接下來我們會對這個初始類一步一步的完善(為了文章易讀后續(xù)接口函數(shù)不會展示類的全貌,只會展示實現(xiàn)的接口函數(shù)內(nèi)容,在文章的末尾會給出完整的string類代碼)如下:
namespace zybjs
{
class string
{
public:
private:
size_t _size;
size_t _capacity;
char* _str;
};
}
2.1 構(gòu)造函數(shù)
(1)默認構(gòu)造和C串構(gòu)造
? ? ? ? 在STL庫中將C串構(gòu)造和默認構(gòu)造分開實現(xiàn),但是我們在模擬實現(xiàn)string類的時候可以將這倆個構(gòu)造函數(shù)合并,這樣的代碼會更簡潔,也方便我們自己的使用。在此之前我們先按庫里面的思路走一趟:在我們給初始容量的時候即便字符串長度是0我們也不能給0,避免后續(xù)無法倍數(shù)擴容,這里我們給的是4。并且給字符串開空間的時候要比容量多一個,最后一個留給'\0'。(VS下實現(xiàn)思路不同,VS下給了一個16字節(jié)的字符數(shù)組_buf,如果要存儲的字符串小于16字節(jié)就存放在字符數(shù)組_buf中,否則就重新開一個空間)
default (1)? ? ? ? ? ? string();
from c - string(2)? string(const char* s);
string()
:_size(0)
,_capacity(4) //不能給0,如果給0后續(xù)無法倍數(shù)擴容 下同
{
_str = new char[_capacity + 1]; //開空間的時候要比容量多一個字節(jié)留給'\0',因為容量的大小不算'\0',下同
_str[0] = '\0';
}
string(const char* str) //const類型接收右值
:_size(strlen(str))
{
_capacity = _size == 0 ? 4 : _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
? ? ? ? ?但是我們在自己實現(xiàn)的時候大可不必這樣去寫,我們之前學(xué)習(xí)了缺省參數(shù),并且全缺省的構(gòu)造函數(shù)也可以作為默認構(gòu)造,所以我們可以對上述代碼進行優(yōu)化,給str一個空串作為缺省參數(shù),這樣當(dāng)我們調(diào)用的時候不給實參就會默認使用空串進行構(gòu)造來完成庫中”string();“的功能。如下:
string(const char* str = "") //const類型接收右值
:_size(strlen(str))
{
_capacity = _size == 0 ? 4 : _size;
_str = new char[_capacity + 1];
strcpy(_str,str);
}
(2)拷貝構(gòu)造
? ? ? ? string類的拷貝構(gòu)造很簡單,要注意的點有倆個:其一,string類涉及到空間管理,所以在拷貝的時候要深拷貝,否則會導(dǎo)致同一空間多次析構(gòu)導(dǎo)致報錯;其二,傳參不能傳值傳參,要使用傳引用傳參,否則會導(dǎo)致無窮遞歸,
string(const string& s)
:_size(s._size)
,_capacity(s._capacity)
{
//深拷貝
_str = new char[_capacity + 1]; //重新開空間
strcpy(_str,s._str); //字符序列拷貝
}
2.2 析構(gòu)函數(shù)
? ? ? ? string類因為涉及到內(nèi)存的管理,所以析構(gòu)函數(shù)不能使用默認生成的析構(gòu),需要我們自己去實現(xiàn)析構(gòu)。在實現(xiàn)析構(gòu)的時候?qū)size和_capacity全部置零,然后通過delete[]釋放_str就可以了。如下:
~string()
{
delete[] _str; //釋放_str
_str = nullptr;
_size = _capacity = 0;
}
2.3 size()、capacity()
? ? ? ? size()和capacity()很多人在實現(xiàn)的時候都覺得很簡單反而會忽略一個點:就是const對象訪問的時候是否會權(quán)限放大。因為這個倆個函數(shù)僅涉及到讀取,沒有修改的操作,所以我們只需要實現(xiàn)const版本來兼容const對象和非const對象。如下
size_t size() const //兼容const對象和非const對象
{
return _size;
}
size_t capacity() const//兼容const對象和非const對象
{
return _capacity;
}
2.4?c_str()
????????返回指向一個數(shù)組的指針,該數(shù)組包含以 null 結(jié)尾的字符序列(即 C 字符串),該序列表示字符串對象的當(dāng)前值。只需完成const版本來兼容const對象和非const對象。指針的返回值也需要是const char *類型,防止通過指針對對象進行修改。
const char* c_str() const
{
return _str;
}
2.5 operator[]
? ? ? ? 因為要實現(xiàn)類似于數(shù)組的訪問方式,所以我們要實現(xiàn)[]的重載形式。[]的特性:能夠隨機訪問元素,對于非const對象能夠修改元素。所以operator[]要實現(xiàn)const版本和非const版本,const版本的返回值必須是const引用。
const char& operator[](size_t pos) const
{
assert(pos < _size&& pos >= 0);
return _str[pos];
}
char& operator[](size_t pos)
{
assert(pos < _size && pos >= 0);
return _str[pos];
}
2.6 operator=
? ? ? ? 賦值運算符的重載是對字符串對象的深拷貝,和拷貝構(gòu)造的過程相同,但是‘=’的特性支持連續(xù)的賦值,所以我們在實現(xiàn)賦值重載的時候需要返回一個string對象來支持賦值重載的連續(xù)賦值。因為我們不涉及對傳入?yún)?shù)的修改,所以我們需要傳入一個const string& 類型。注意在賦值之前需要釋放原來的空間。如下:
string& operator=(const string& s)
{
if (s._str != _str)
{
_size = s._size;
_capacity = s._capacity;
//深拷貝
char* _tmp = new char[_capacity + 1];
strcpy(_tmp,s._str);
//先釋放原來的空間
delete[] _str;
_str = _tmp;
}
return *this;
}
測試:
zybjs::string s1("cacaca");
zybjs::string s2("dadada");
zybjs::string s3("bababa");
s3 = s2 = s1;
std::cout << s1.c_str() << std::endl;
std::cout << s2.c_str() << std::endl;
std::cout << s3.c_str() << std::endl;
2.7 字符串比較?
? ? ? ? 字符串比較我們通過strcmp來進行比較(strcmp(srt1,str2)),返回的值為0,字符串相同;返回的值大于0,str1>str2;返回的值小于0,str1<str2?;诖吮憧梢詫崿F(xiàn)字符串的比較函數(shù)。
bool operator>(const string& s) const
{
return strcmp(_str, s._str) > 0;
}
bool operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s) const
{
//return *this > s || *this == s;
return *this > s || s == *this;
}
bool operator<(const string& s) const
{
return !(*this >= s);
}
bool operator<=(const string& s) const
{
return !(*this > s);
}
bool operator!=(const string& s) const
{
return !(*this == s);
}
2.8 迭代器實現(xiàn)
? ? ? ? string的迭代器底層是一個指針,string類的迭代器是一種用于訪問字符串中字符的對象,可以通過迭代器的運算符訪問字符串中的字符。迭代器為C++容器提供了一種通用的訪問手段。
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
//指向第一個字符的位置
return _str;
}
iterator end()
{
//指向最后一個字符的后一個位置
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
2.9 reserve
? ? ? ??reserve()是為了給對象預(yù)留空間,如果我們提前得知字符串需要的空間我們就可以提前開好,避免頻繁擴容帶來的性能消耗。當(dāng)reserve的參數(shù)小于string底層空間大小的時候,reserve就不會對容量進行處理。
void reserve(size_t n)
{
if (n > _capacity)
{
char* _tmp = new char[n+1];
strcpy(_tmp, _str);
delete[] _str;
_str = _tmp;
_capacity = n;
}
}
2.10 resize
? ? ? ? ??修改字符有效個數(shù)為n,多出的空間用字符ch填充。如果n小于有效字符數(shù),本質(zhì)就是刪字符操作。如果容量不夠會擴容。
void resize(size_t n, char ch = '\0')
{
//當(dāng)n<有效字符數(shù)的時候本質(zhì)上就是刪字符
//但是我們一般不會進行縮容,在原來的空間上將n位置的字符設(shè)置為'\0'
if (n < _size)
{
_size = n;
_str[_size] = '\0';
}
else if (n > _size)
{
if (n > _capacity) //n>_capacity就進行擴容
{
reserve(n);
}
size_t i = _size;
while (i < n) //將非有效字符初始化為ch
{
_str[i] = ch;
i++;
}
_size = n;
_str[_size] = '\0'; //設(shè)置終止位
}
}
2.11 push_back、append、operator+=
? ? ? ? push_back尾插,append是在字符串后面追加一個字符串,實現(xiàn)比較簡單,但要注意檢查容量。
void push_back(char ch)
{
if (_size + 1 > _capacity)
{
reserve(2 * _capacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str+_size,str);
_size += len;
}
? ? ? ? operator+=的功能可以由push_back和append的復(fù)用來實現(xiàn):
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
2.12 insert
? ? ? ? insert是string類中支持pos位插入字符或者字符串的函數(shù)。其中,pos表示插入的位置,返回string的引用表示插入后的新字符串。
string& insert(size_t pos, char ch)
{
if (_size + 1 > _capacity)
{
reserve(2 * _capacity);
}
size_t n = pos;
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
_size += 1;
_str[_size] = '\0';
return *this;
}
string& insert(size_t pos, const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_capacity + len);
}
size_t n = pos;
size_t end = _size + 1;
while (end > pos)
{
_str[end - 1 + len] = _str[end - 1];
end--;
}
strncpy(_str + pos, str, len);
_size += len;
_str[_size] = '\0';
return *this;
}
2.13?erase
? ? ? ??C++中的string類提供了一個名為erase()的成員函數(shù),用于刪除字符串中的一部分字符并修改該字符串。該函數(shù)可以接受1個或2個參數(shù),具體取決于要刪除的字符數(shù)。如果沒有顯式的指定刪除字符數(shù),會使用默認的npos也就是無符號整型-1來作為缺省參數(shù)(65535),就會默認刪除pos位后面所有的字符。然后返回刪除字符后生成的新字符。
? ? ? ? 首先我們要定義一個和庫里相同的npos:
string& erase(size_t pos,size_t len = npos)
{
assert(pos < _size);
if (len >= _size - pos - 1)
{
_size = pos;
_str[_size] = '\0';
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
2.14 流插入和流提取
????????文章來源:http://www.zghlxwxcb.cn/news/detail-698694.html
std::ostream& operator<<(std::ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
std::istream& operator>>(std::istream& in, string& s)
{
s.clear(); //輸入之前要清空字符串
char ch = in.get();//獲取字符包括'\n'
char buff[32]; //設(shè)置緩沖區(qū)來防止頻繁擴容
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i] = ch;
if (i == 30)
{
buff[31] = '\0';
s += buff;
i = 0;
}
i++;
ch = in.get();
}
buff[i] = '\0';
s += buff;
}
3. 完整代碼(均調(diào)試通過)
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cassert>
namespace zybjs
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
//指向第一個字符的位置
return _str;
}
iterator end()
{
//指向最后一個字符的后一個位置
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
//string()
// :_size(0)
// ,_capacity(4) //不能給0,如果給0后續(xù)無法倍數(shù)擴容 下同
//{
// _str = new char[_capacity + 1]; //開空間的時候要比容量多一個字節(jié)留給'\0',因為容量的大小不算'\0',下同
// _str[0] = '\0';
//}
//string(const char* str)
// :_size(strlen(str))
//{
// _capacity = _size == 0 ? 4 : _size;
// _str = new char[_capacity + 1];
// strcpy(_str, str);
//}
string(const char* str = "") //const類型接收右值
:_size(strlen(str))
{
_capacity = _size == 0 ? 4 : _size;
_str = new char[_capacity + 1];
strcpy(_str,str);
}
string(const string& s)
:_size(s._size)
,_capacity(s._capacity)
{
//深拷貝
_str = new char[_capacity + 1]; //重新開空間
strcpy(_str,s._str); //字符序列拷貝
}
~string()
{
delete[] _str; //釋放_str
_str = nullptr;
_size = _capacity = 0;
}
size_t size() const //兼容const對象和非const對象
{
return _size;
}
size_t capacity() const//兼容const對象和非const對象
{
return _capacity;
}
const char* c_str() const
{
return _str;
}
const char& operator[](size_t pos) const
{
assert(pos < _size&& pos >= 0);
return _str[pos];
}
char& operator[](size_t pos)
{
assert(pos < _size && pos >= 0);
return _str[pos];
}
string& operator=(const string& s)
{
if (s._str != _str)
{
_size = s._size;
_capacity = s._capacity;
//深拷貝
char* _tmp = new char[_capacity + 1];
strcpy(_tmp,s._str);
//先釋放原來的空間
delete[] _str;
_str = _tmp;
}
return *this;
}
//字符串比較
bool operator>(const string& s) const
{
return strcmp(_str, s._str) > 0;
}
bool operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s) const
{
//return *this > s || *this == s;
return *this > s || s == *this;
}
bool operator<(const string& s) const
{
return !(*this >= s);
}
bool operator<=(const string& s) const
{
return !(*this > s);
}
bool operator!=(const string& s) const
{
return !(*this == s);
}
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')
{
//當(dāng)n<有效字符數(shù)的時候本質(zhì)上就是刪字符
//但是我們一般不會進行縮容,在原來的空間上將n位置的字符設(shè)置為'\0'
if (n < _size)
{
_size = n;
_str[_size] = '\0';
}
else if (n > _size)
{
if (n > _capacity) //n>_capacity就進行擴容
{
reserve(n);
}
size_t i = _size;
while (i < n) //將非有效字符初始化為ch
{
_str[i] = ch;
i++;
}
_size = n;
_str[_size] = '\0'; //設(shè)置終止位
}
}
void push_back(char ch)
{
if (_size + 1 > _capacity)
{
reserve(2 * _capacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str+_size,str);
_size += len;
}
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)
{
if (_size + 1 > _capacity)
{
reserve(2 * _capacity);
}
size_t n = pos;
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
_size += 1;
_str[_size] = '\0';
return *this;
}
string& insert(size_t pos, const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_capacity + len);
}
size_t n = pos;
size_t end = _size + 1;
while (end > pos)
{
_str[end - 1 + len] = _str[end - 1];
end--;
}
strncpy(_str + pos, str, len);
_size += len;
_str[_size] = '\0';
return *this;
}
string& erase(size_t pos,size_t len = npos)
{
assert(pos < _size);
if (len >= _size - pos - 1)
{
_size = pos;
_str[_size] = '\0';
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
void clear()
{
_size = 0;
_str[_size] = '\0';
}
private:
size_t _size;
size_t _capacity;
char* _str;
static const size_t npos;
};
const size_t string::npos = -1;
std::ostream& operator<<(std::ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
std::istream& operator>>(std::istream& in, string& s)
{
s.clear(); //輸入之前要清空字符串
char ch = in.get();//獲取字符包括'\n'
char buff[32]; //設(shè)置緩沖區(qū)來防止頻繁擴容
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i] = ch;
if (i == 30)
{
buff[31] = '\0';
s += buff;
i = 0;
}
i++;
ch = in.get();
}
buff[i] = '\0';
s += buff;
}
}
?文章來源地址http://www.zghlxwxcb.cn/news/detail-698694.html
到了這里,關(guān)于【C++精華鋪】10.STL string模擬實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!