放在前面:
我們實現(xiàn)string類, 并不是跟源碼一模一樣(并不是造一個新的輪子)
, 只是了解每個接口的實現(xiàn)過程
? 我們以后也用的放心
(比如時間復(fù)雜度, 空間復(fù)雜度等等)
基本結(jié)構(gòu)
private:
size_t _size; // 實際長度
size_t _capacity; // 空間
char* _str;
習(xí)慣把不可能為負數(shù)的值的類型 定義為 size_t
天選之子
構(gòu)造函數(shù)
- 考慮到 無參調(diào)用和有參調(diào)用 && 只有一個參數(shù) ? 我們可以采用
全缺省的形式
- 傳參類型應(yīng)該為
常量字符串
? const char* ? 一般用于初始化, 咋們給的值都是常量 -
缺省值初始化為 ""(空字符串)
? 常量字符串默認就會有\0
, 即 “” (空字符串) 里面就是一個\0
- _size 和 _capacity 的大小不包括
\0
? 所以, 我們初始化長度的時候, 用 strlen(str) - _str要先開空間
??????
string(const char* str = "")
:_size(strlen(str))
,_capacity(_size)
,_str(new char[_capacity+1])
{
memcpy(_str, str, _capacity+1);
}
注意:
初始化的順序是按照 聲明的順序來的
? 我們盡量保持 初始化和聲明的順序一致, 要不然就會出現(xiàn)問題- 由于 _size 和 _capacity不包括
\0
的長度 ? 我們_str開空間的時候要多開一個, 給\0
用
???為啥要用 memcpy函數(shù)? 為啥不用strcpy函數(shù)呢?
-
1. memcpy函數(shù) 和 strcpy函數(shù)的
區(qū)別
: memcpy函數(shù)是逐個字節(jié)
進行copy的, 而strcpy是遇到 \0就停止
copy
2 我們標(biāo)準庫中 string類的輸出是按照_size
來的. 即遇到下面的情況, strcpy 和 strcpy的區(qū)別就體現(xiàn)出來了??????
// 字符串如下:
// hello
// world!
// 來做以下幾組實驗
// 1. 庫里的string
std::string str1 = "hello";
str1 += "\0";
str1 += "world!";
cout << str1 << endl;
// 2. 用strcpy來實現(xiàn)
// ...
//..
// 3. 用memcpy來實現(xiàn)
// ...
// ...
*****
1. helloworld!
2. hello
3. helloworld!
*****
- memcpy默認是不會copy
\0
, 所以memcpy函數(shù)里面的長度 傳的是_capacity+1
析構(gòu)函數(shù)
~string()
{
delete[] _str; // 清理動態(tài)申請空間
// 置零(置空)
_str = nullptr;
_size = _capacity = 0;
}
拷貝構(gòu)造函數(shù)
- 我們不寫構(gòu)造函數(shù), 系統(tǒng)自動生成的是一種
淺拷貝
? 對有動態(tài)資源申請的對象來說, 會對同一塊空間析構(gòu)兩次 - 我們寫的是
深拷貝
? 找一塊新的空間給this->_str
, 然后將s的內(nèi)容
copy過去, 更新_capacity 和 _size
String(const string& s)
{
_str = new char[s._capacity + 1];
memcpy(_str, s._str, s._capacity + 1);
_capacity = s._capacity;
_size = s._size;
}
空間
size()函數(shù)
size_t size()const
{
return _size;
}
capacity()函數(shù)
size_t capacity()const
{
return _capacity;
}
clear()函數(shù)
void clear()
{
_size = 0;
_str[_size] = '\0';
}
clear()函數(shù) 并不是用來清理空間的, 而是讓空間置空(置零)
empty()函數(shù)
bool empty()const
{
if(_size == 0)
return true;
return false;
}
reverse()函數(shù)
void reverse(size_t n)
{
assert(n >= 0);
// 擴容邏輯 -- 一般我們不進行縮容
if(n > _capacity)
{
char* tem = new char[n+1];
memcpy(tem._str, _str, _capacity+1);
delete[] _str;
_str = tem;
_capacity = n;
}
}
resize()函數(shù)
void resize(size_t n, char ch = '\0')
{
assert(n >= 0);
if(n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
reverse(n);
for(int i = _size; i < _size+n; i++)
{
_str[i] = ch;
}
_size = _size + n;
_str[_size] = '\0';
}
}
迭代器
迭代器是屬于類的 ? 我們聲明迭代器的時候要聲明類域
??????
std::string str = "hello world";
iterator it = str.begin();
*****
error C2955: “std::iterator”: 使用 類 模板 需要 模板 參數(shù)列表
*****
但要在 string類里面,定義一種類型, 有兩種方式:
- typedef 一個變量
- 定義一個內(nèi)部類 (
內(nèi)部類一般都是自定義類型
)
而我們這里iterator其實就是數(shù)組_str各個下標(biāo)對應(yīng)的地址, 是一種 內(nèi)置類型
? 所以, 我們采用typedef的方式來實現(xiàn) iterator
iterator
typedef char* iterator;
begin()函數(shù)
iterator begin()
{
return _str;
}
end()函數(shù)
iterator end()
{
return _str + _size;
}
const_iterator
typedef const char* const_iterator;
begin()函數(shù)
const_iterator begin()const
{
return _str;
}
end()函數(shù)
const_iterator end()const
{
return _str + _size;
}
增
push_back()函數(shù)
尾插一個字符的操作:
-
是否需要擴容 ? _size == _capacity
-
擴容邏輯:
- _capacity == 0 ? 傳個 4 過去擴容
- _capacity > 0 ? 2倍擴容
-
_size++, _str[_size] = ‘\0’;
void push_back(const char ch)
{
// 是否擴容
if (_size == _capacity)
{
size_t newcapacity = (_capacity == 0 ? 4 : _capacity * 2);
reverse(newcapacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
append()函數(shù)
尾插一個字符串的操作:
-
是否需要擴容
-
擴容邏輯:
1. _size + len <= _capacity — — 不需要擴容
2. _size + len > _capacity — — 擴容(_size + len) -
_size = _size + len, _str[_size] = ‘\0’;
void append(const char* ch)
{
size_t len = strlen(ch);
// 是否擴容
if (len + _size > _capacity)
{
reverse(len + _size);
}
for (size_t i = 0; i < len; i++)
{
_str[_size + i] = ch[i];
}
_size += len;
_str[_size] = '\0';
}
operator+=
復(fù)用
push_back() 和 append()
void operator+=(const char ch)
{
push_back(ch);
}
void operator+=(const char* ch)
{
append(ch);
}
insert()函數(shù)
在 下標(biāo)為pos的位置插入n個字符:
-
是否需要擴容
-
擴容邏輯:
- _size + n <= _capacity — — 不需要擴容
- _size + n > _capacity — — 擴容(_size + n)
-
挪動數(shù)據(jù)
-
_size = _size + n, _str[_size] = ‘\0’;
void insert(size_t pos, const char* ch)
{
assert(pos >= 0);
// 是否需要擴容
size_t len = strlen(ch);
if (_size + len > _capacity)
{
reverse(_size + pos);
}
// 挪動數(shù)據(jù)
size_t end = _size;
// 挪動數(shù)據(jù)時, 下標(biāo)不能小于0(即不能等于 -1)
while (end >= pos && end != _nops)
{
_str[end + len] = _str[end];
end--;
}
// 插入數(shù)據(jù)
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = ch[i];
}
_size = _size + len;
}
- 對了, 這里的 _nops是我么定義的一個靜態(tài)成員變量
// 類里面的聲明
public:
static size_t _nops;
// 類外面的初始化
size_t muyu::string::_nops = -1; // 這里的muyu是我定義的一個命名空間域
???為啥要定義一個nops? 為啥要初始化為 -1?
-
前面, 我們有說過:
不可能為負數(shù)的, 我們定義成 size_t (無符號整數(shù))
如果 下標(biāo)減到 -1 — ---- 由于是 size_t, 變量是不會比 -1 小的
那么 size_t 類型如何區(qū)分開 -1 呢?
size_t i = -1; ? i 等于 2 ^ 32 -1;
那么 下標(biāo) 不等于 nops不就行了~~
還有就是, 插入函數(shù) 和 刪除函數(shù)中 字符串的長度如果不寫, 就是nops
刪
erase()函數(shù)
void erase(size_t pos, size_t n = _nops)
{
assert(pos >= 0);
// 是否把pos位置后面全部刪除
if (n == _nops || pos + n >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
for (size_t i = pos; i < pos + n; i++)
{
_str[i] = _str[i + n];
}
_size = _size - n;
}
}
查
find()函數(shù)
size_t find(size_t pos = 0, const char ch )
{
assert(pos < _size);
for (int i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return _nops;
}
size_t find(size_t pos = 0, const char* ch )
{
assert(pos < _size);
// 如果找到返回地址, 否則返回nullptr
const char* res = strstr(_str, ch);
if (res)
{
return res - _str;
}
else
{
return _nops;
}
}
改
swap()函數(shù)
void swap(string& s)
{
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
std::swap(_str, s._str);
}
operator[]函數(shù)
//不是&, 那就返回的是常量臨時拷貝
char& operator[](size_t n)
{
assert(n <= _size);
return _str[n];
}
const char& operator[](size_t n)const
{
assert(n <= _size);
return _str[n];
}
operator= 函數(shù)
//string& operator=(const string& s)
//{
// // 傳統(tǒng)寫法 -- 找一塊空間, 把s的內(nèi)容搞過去, 然后和*this交換
// // 1. 找空間, 移內(nèi)容; 2. 釋放this的空間
// string tem;
// tem.reverse(s._capacity + 1);
// memcpy(tem._str, s._str, s._capacity + 1);
// tem._size = s._size;
// swap(tem);
// return *this;
//}
string& operator=(string s)
{
swap(s);
return *this;
}
比較
bool operator==(const string& s)
{
// 如果_size都不相等, 那么何談相等
return _size == s._size &&
memcmp(_str, s._str, _size) == 0;
}
bool operator>(const string& s)
{
// 取較小長度進行比較
size_t size = std::min(_size, s._size);
int ret = memcmp(_str, s._str, size);
// 由于是取較小長度進行比較, 那么會出現(xiàn)如下幾種情況:
// 1. str1 = hello, str2 = hello
// 2. str1 = hello\0xxx, str2 = hello
// 3. str1 = hello, str2 = hello\00xxx
// 這幾種情況都是根據(jù)較小長度比較的結(jié)果都是 相等
if (ret == 0)
{
if (_size > s._size)
return true;
else
return false;
}
return ret > 0;
}
bool operator!=(const string& s)
{
return !(*this == s);
}
bool operator>=(const string& s)
{
return *this == s || *this > s;
}
bool operator<(const string& s)
{
return !(*this >= s);
}
bool operator<=(const string& s)
{
return !(*this > s);
}
流操作
流操作要寫在全局位置 ? cout/cin 要搶占第一個參數(shù). 若要是在類中, 第一個參數(shù)就默認是this文章來源:http://www.zghlxwxcb.cn/news/detail-727558.html
流插入 <<
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
流提取 >>
istream& operator>>(istream& in, string& s)
{
// 每一次新的讀取要進行清理一下
// 要不然就會接著讀取, 而不是覆蓋
s.clear();
// get()函數(shù)可以讀到每一個字符, 包括空格 和 換行
char ch = in.get();
// 處理前緩沖區(qū)前面的空格或者換行
while (ch == ' ' || ch == '\n')
{
ch = in.get();
}
// in >> ch;
char buff[128]; // buff數(shù)組的作用是: 減少開空間的次數(shù)
int i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
s += buff;
i = 0;
}
//in >> ch;
ch = in.get();
}
// 如果最后 buff數(shù)組還有數(shù)據(jù), 那么就加到s中
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
C接口
c_str()函數(shù)
const char* c_str()const
{
return _str;
}
substr()函數(shù)
string substr(size_t pos = 0, size_t n = _nops)
{
assert(pos >= 0);
// 是否需要擴容
int len = n; // 默認是n
if (n == _nops || pos + n >= _size)
{
len = _size - pos;
}
string tem;
tem.reverse(len);
//for (size_t i = pos; i < len; i++)
//{
// tem[i] = _str[i + pos];
//}
//tem._size = len;
//tem[_size] = '\0';
for (size_t i = pos; i < pos + len; i++)
{
tem += _str[i];
}
return tem;
}
源碼
#pragma once
#include <string.h>
#include<assert.h>
#include<iostream>
namespace muyu
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
// friend ostream& operator<<(ostream& out, const string& s);
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 = "")
:_size(strlen(str))
,_capacity(_size)
,_str(new char[_capacity+1])
{
memcpy(_str, str, _capacity+1);
}
string(const string& tem)
{
_str = new char[tem._capacity + 1];
memcpy(_str, tem._str, tem._capacity + 1);
_capacity = tem._capacity;
_size = tem._size;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
const char* c_str()const
{
return _str;
}
void reverse(size_t n)
{
if (n > _capacity)
{
char* tem = new char[n + 1];
memcpy(tem, _str, _capacity + 1);
_capacity = n;
delete[] _str;
_str = tem;
}
}
void resize(size_t n, char ch = '\0')
{
if (_size > n)
{
_str[n] = '\0';
_size = n;
}
else
{
reverse(n); // 不管需不需要擴容,都丟給reverse. reverse內(nèi)部有判斷是否需要擴容
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_str[n] = '\0';
}
}
void push_back(const char ch)
{
// 是否擴容
if (_size == _capacity)
{
size_t newcapacity = (_capacity == 0 ? 4 : _capacity * 2);
reverse(newcapacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
void append(const char* ch)
{
size_t len = strlen(ch);
// 是否擴容
if (len + _size > _capacity)
{
reverse(len + _size);
}
for (size_t i = 0; i < len; i++)
{
_str[_size + i] = ch[i];
}
_size += len;
_str[_size] = '\0';
}
void operator+=(const char ch)
{
push_back(ch);
}
void operator+=(const char* ch)
{
append(ch);
}
void insert(size_t pos, const char* ch)
{
assert(pos >= 0);
// 是否需要擴容
size_t len = strlen(ch);
if (_size + len > _capacity)
{
reverse(_size + pos);
}
// 挪動數(shù)據(jù)
size_t end = _size;
// 挪動數(shù)據(jù)時, 下標(biāo)不能小于0(即不能等于 -1)
while (end >= pos && end != _nops)
{
_str[end + len] = _str[end];
end--;
}
// 插入數(shù)據(jù)
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = ch[i];
}
_size = _size + len;
}
void erase(size_t pos, size_t n = _nops)
{
assert(pos >= 0);
if (n == _nops || pos + n >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
for (size_t i = pos; i < pos + n; i++)
{
_str[i] = _str[i + n];
}
_size = _size - n;
}
}
size_t size()const
{
return _size;
}
void clear()
{
_size = 0;
_str[_size] = '\0';
}
bool empty()const
{
return _size > 0;
}
void swap(string& s)
{
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
std::swap(_str, s._str);
}
//不是&, 那就返回的是常量臨時拷貝
char& operator[](size_t n)
{
assert(n <= _size);
return _str[n];
}
const char& operator[](size_t n)const
{
assert(n <= _size);
return _str[n];
}
string substr(size_t pos = 0, size_t n = _nops)
{
assert(pos >= 0);
int len = n; // 默認是n
if (n == _nops || pos + n >= _size)
{
len = _size - pos;
}
string tem;
tem.reverse(len);
//for (size_t i = pos; i < len; i++)
//{
// tem[i] = _str[i + pos];
//}
//tem._size = len;
//tem[_size] = '\0';
for (size_t i = pos; i < pos + len; i++)
{
tem += _str[i];
}
return tem;
}
bool operator==(const string& s)
{
return _size == s._size &&
memcmp(_str, s._str, _size) == 0;
}
bool operator>(const string& s)
{
// 取較小長度進行比較
size_t size = std::min(_size, s._size);
int ret = memcmp(_str, s._str, size);
if (ret == 0)
{
if (_size > s._size)
return true;
else
return false;
}
return ret > 0;
}
bool operator!=(const string& s)
{
return !(*this == s);
}
bool operator>=(const string& s)
{
return *this == s || *this > s;
}
bool operator<(const string& s)
{
return !(*this >= s);
}
bool operator<=(const string& s)
{
return !(*this > s);
}
size_t find(const char ch, size_t pos = 0)
{
assert(pos < _size);
for (int i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return _nops;
}
size_t find(const char* ch, size_t pos = 0)
{
assert(pos < _size);
const char* res = strstr(_str, ch);
if (res)
{
return res - _str;
}
else
{
return _nops;
}
}
//string& operator=(const string& s)
//{
// // 傳統(tǒng)寫法 -- 找一塊空間, 把s的內(nèi)容搞過去, 然后和*this交換
// // 1. 找空間, 移內(nèi)容; 2. 釋放this的空間
// //string tem;
// //tem.reverse(s._capacity + 1);
// //memcpy(tem._str, s._str, s._capacity + 1);
// //tem._size = s._size;
// //swap(tem);
// //return *this;
//}
string& operator=(string s)
{
swap(s);
return *this;
}
private:
size_t _size;
size_t _capacity;
char* _str;
public:
static size_t _nops;
};
size_t string::_nops = -1;
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
istream& operator>>(istream& in, string& s)
{
// 每一次新的讀取要進行清理一下
// 要不然就會接著讀取, 而不是覆蓋
s.clear();
// get()函數(shù)可以讀到每一個字符, 包括空格 和 換行
char ch = in.get();
// 處理前緩沖區(qū)前面的空格或者換行
while (ch == ' ' || ch == '\n')
{
ch = in.get();
}
// in >> ch;
char buff[128]; // buff數(shù)組的作用是: 減少開空間的次數(shù)
int i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
s += buff;
i = 0;
}
//in >> ch;
ch = in.get();
}
// 如果最后 buff數(shù)組還有數(shù)據(jù), 那么就加到s中
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
}
持志如心痛. — — 王陽明
譯:心在痛上,豈有工夫說閑話、管閑事.文章來源地址http://www.zghlxwxcb.cn/news/detail-727558.html
到了這里,關(guān)于[C++隨筆錄] string模擬實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!