String容器的底層實作
- String
- 類成員
- 建構式和拷貝建構式
- 迭代器
- 函式功能
- 完整代碼
- 總結
String
String是STl中的六大組件之一,里面可以存放字串,在c語言中同樣可以用陣列來存放字串,但是c語言中無法直接使用函式對字串進行操作,所以使用String類可以避免這些,而這里也符合了c++封裝的思想,
在String類中增加了許多介面,專門對字串進行操作
類成員
String類主要的成員有三個,一個指標,一個有效字符長度,一個容量大小
指標指向的空間用來存放字串
private:
char* _str;
size_t _size;
size_t _capacity;
建構式和拷貝建構式
string(char* str = "")//在無參傳入時候,以空字符傳入,使用半預設構造
:_size(strlen(str))//這里的初始化串列的順序只和宣告類成員順序有關
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
~string() {//解構式
delete[] _str;//因為是進行new char[_capacity + 1];所以在釋放空間時,
_capacity = 0;//delete后面需要加[]
_str = nullptr;
}
void swap(string& s) {
::swap(_str, s._str);//使用的是庫函式的swap函式
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// s2(s1)現代建構式方法
string(const string& s)//傳入一個string物件
:_str(nullptr)
{
string tmp(s._str);//創建一個tmp變數,將s的字串傳入
//this->swap(tmp);
swap(tmp);//這里進行交換
}
// s2 = s1;現代拷貝建構式方法
string& operator = (string str) {//這里進行了傳值,會進行一次建構式來開空間,
swap(str); //所以可以直接使用swap函式來進行交換
return *this;
}
我們需要注意的是,在string類中,在創建,拷貝,傳回傳值等都是會深拷貝,所以這時候參考(&)就顯得尤為重要,它可以避免我們進行深拷貝,去創建空間,而是直接使用
迭代器
typedef char* iterator; //由于我們有時候可能會出現我可以進行讀寫操作,或
typedef const char* const_iterator;//者只能進行讀操作,所以會有const的出現
iterator begin() {//回傳頭節點
return _str;
}
iterator end() {//回傳尾節點
return _str + _size;
}
const_iterator begin() const{
return _str;
}
const_iterator end() const{
return _str + _size;
}
在string類中,迭代器本身其實是指標,但是,在別的容器里迭代器本不是指標,至于是什么,在list容器中我會進行解釋,迭代器其實還有很多操作,比如+,+=,>>,<<等等,他將我們大多的運算子全都多載一遍,使我們用起來和陣列類似,但在這里我沒有展示
函式功能
基本功能有尾插,擴容,插入字串,插入字符,洗掉字符,開空間
void reserve(size_t n) {//擴容
if (n > _capacity) {//判斷容量是否超過
char* tmp = new char[n + 1];
strcpy(tmp, _str);//如果超過了話,則重新創建一個更大的空間,在把之前空間的內容
delete[] _str;//復制到新的空間中去
_str = tmp;
_capacity = n;
}
};
void push_back(char c) {//尾插,這里和append都可以使用insert函式進行復寫
if (_size >= _capacity) {
reserve(_capacity * 2);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
void append(const char* c) {//插字串
size_t len = strlen(c);
if (_size + len >= _capacity) {
reserve(_size + len);
}
strcpy(_str + _size, c);
_size += len;
}
//const char& operator[] (size_t num) 只能讀
char& operator[] (size_t num) {//同陣列一樣,可讀可寫
return *(_str + num);
}
void resize(size_t n, char ch = '\0'){
if (n <= _size) {//當需要的有效空間小于等于_size時,會直接插入
for (size_t i = 0; i < n; i++)
*(_str + i) = ch;
_size = n;
_str[_size] = '\0';
}
else {//當需要的有效空間大于_size,則要先判斷是否要重新擴容,再寫入ch
if (n > _capacity) {
reserve(n);
}
for (size_t i = _size; i < n; i++)//寫入ch
*(_str + i) = ch;
_size = n;
_str[_size] = '\0';
}
}
//值得注意的是,resize函式,在寫入多個'\0'時,即使顯示出來的有效字符不變,
//但實際上他并不會在第一個\0'位置停止讀取,后面的'\0'仍然算有效字符
size_t size() const{
return _size;
}
size_t capacity() const {
return _capacity;
}
string& insert(size_t pos, char ch) {//插入字符
assert(pos <= _size);
if (_size >= _capacity) {//可能會存在_capacity是0的情況,
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;//為了避免_capacity是0而產生的報錯
reserve(_size);
}
size_t newnode = _size + 1;
while (newnode > pos) {//移動位置
_str[newnode] = _str[newnode - 1];
--newnode;
}
_str[pos] = ch;
_size++;
return *this;//因為在主函式內變數仍然存在,所以需要*this,同樣,回傳型別得是string&
}
string& insert(size_t pos, const char* str) {//插入字串
assert(pos <= _size);
size_t len = strlen(str);
if (_size+len >= _capacity) {
reserve(_size+len);
}
size_t newnode = _size + len;
while (newnode >= pos+len) {
_str[newnode] = _str[newnode-len];
newnode--;
}
for (size_t i = 0; i < len; ++i)//把str的字串寫入_str中
{
_str[pos + i] = str[i];
}
_size += len;
return *this;
}
string& erase(size_t pos, size_t len = npos) {//洗掉字串
assert(pos < _size);
if (pos + len >= _size || len == pos) {
_str[0] = '\n';
_size = npos;
}
else {
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
//這里的nops,其實在類的private中定義的全域變數
//static const size_t npos;,
//其次他的初始化是在類的外部定義的, const size_t string::npos = -1;
在VS19里面,空間的擴容基本上是以1.5倍的方式進行的,但在別的環境中又不一樣,比如在linux下是以2倍的方式進行擴容
完整代碼
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(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
~string() {
delete[] _str;
_capacity = 0;
_str = nullptr;
}
void swap(string& s) {
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// s2(s1)
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
//this->swap(tmp);
swap(tmp);
}
// s2 = s1;
string& operator = (string str) {
swap(str);
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 push_back(char c) {//尾插
if (_size >= _capacity) {
reserve(_capacity * 2);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
void append(const char* c) {//插字串
size_t len = strlen(c);
if (_size + len >= _capacity) {
reserve(_size + len);
}
strcpy(_str + _size, c);
_size += len;
}
char& operator[] (size_t num) {//同陣列一樣
return *(_str + num);
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size) {
for (size_t i = 0; i < n; i++)
*(_str + i) = ch;
_size = n;
_str[_size] = '\0';
}
else {
if (n > _capacity) {
reserve(n);
}
for (size_t i = _size; i < n; i++)
*(_str + i) = ch;
_size = n;
_str[_size] = '\0';
}
}
//string& operator+=(char ch)
size_t size() const{
return _size;
}
size_t capacity() const {
return _capacity;
}
string& insert(size_t pos, char ch) {
assert(pos <= _size);
if (_size >= _capacity) {
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(_size);
}
size_t newnode = _size + 1;
while (newnode > pos) {
_str[newnode] = _str[newnode - 1];
--newnode;
}
_str[pos] = ch;
_size++;
return *this;
}
string& insert(size_t pos, const char* str) {
assert(pos <= _size);
size_t len = strlen(str);
if (_size+len >= _capacity) {
reserve(_size+len);
}
size_t newnode = _size + len;
while (newnode >= pos+len) {
_str[newnode] = _str[newnode-len];
newnode--;
}
for (size_t i = 0; i < len; ++i)
{
_str[pos + i] = str[i];
}
_size += len;
return *this;
}
string& erase(size_t pos, size_t len = npos) {
assert(pos < _size);
if (pos + len >= _size || len == pos) {
_str[0] = '\n';
_size = npos;
}
else {
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
void clear() {
_str[0] = '\0';
_size = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;
static const size_t npos;
};
const size_t string::npos = -1;
總結
string容器的底層邏輯其實非常簡單,他利用陣列存盤字串,但外加各種介面來方便使用,并且多載運算子,將多載后的運算子功能和本身功能一樣,由于string物件都是進行深拷貝,所以我們有時候可以利用參考,來減少開空間的負擔
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/357233.html
標籤:其他
上一篇:樹莓派入門(保姆級)
