
string
- 1.前言
- 2.string類常用介面實作
- 3.總結
1.前言
之前學到了string類常用介面的后,我很好奇string類在底層是怎樣實作的,在由之前學習到c++有關的之后,所以我打算模擬實作一下string類常用的介面,以便加深我對之前的知識的理解,和更好的去使用string類,比如在哪些場景使用哪些介面較為合適,接下來我會用c++來模擬實作各個介面,
2.string類常用介面實作
string類就是存盤字符的順序表,它的實作與與順序表的實作是非常相似,主要介面為增刪查改,實作如下
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;
//定義一個屬于自己的域
namespace sjp
{
class string
{
private:
char* _str;
size_t _size;//字串中的有效的字符個數
size_t _capacity;//字串中的最大容量
static const size_t npos;//npos是string類物件中的一個靜態成員變數,為整數的最大值
public:
//建構式
string(const char* str ="")
{
_size = strlen(str);//計算出初始化字串的大小
_capacity = _size;
_str = new char[_capacity+1];//開辟空間多開辟一塊空間,這塊空間留給\0
strcpy(_str, str);
}
//交換函式,可以與另一個sting物件交換資料
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//解構式,清理資源
~string()
{
delete _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
//拷貝構造,物件創建時,可以將另一個string物件的資料拷貝給要創建的物件
string(const string& s)
{
sjp::string tmp(s._str);
swap(tmp);//函式呼叫結束時tmp會被解構式給清理掉
}
//迭代器,string的迭代器的底層就是指標
typedef char* iterator;
iterator begin()
{
return this->_str;//回傳字串首元素的地址
}
iterator end()
{
return this->_str + _size;//回傳字串最后一個地址
}
// 回傳字符的個數
size_t size()
{
return _size;
}
//預留字串的空間,只改變_capacity,不改變初始化空間
void reserve(size_t capacity)
{
//如果預留的空間大于原本的空間,直接開辟一塊capacity的空間,并把資料拷貝給這塊空間
if (capacity > _capacity)
{
char* str= new char[capacity];
strcpy(str, _str);
_str = str;
_capacity = capacity;
}
//如果capacity小于_size,則_capacity不做改變,_size改變即可
else if(capacity<_size)
{
_size = capacity;
_str[_size] = '\0';
}
}
//預留空間,并且初始化這塊空間,既改變capacity和size
void resize(size_t capacity,char ch='\0')
{
if (capacity < _size)
{
_size = capacity;
_str[_size] = '\0';
}
else
{
if (capacity > _capacity)
{
reserve(capacity);
}
memset(_str + _size, ch, (capacity-_size));//memset是創建一塊空間,并初始化這塊空間
_str[_size] = '\0';
_size = capacity;
}
}
//尾插一個字符
void push_back(char ch)
{
if (_size == _capacity)//判斷字串的空間是否滿了
{
size_t newcapacity = _capacity == 0 ? 5 : _capacity * 2;
reserve(newcapacity);
_capacity = newcapacity;
}
_str[_size] = ch;
_size += 1;
_str[_size] = '\0';//最后記得加上\0
}
//尾插一個字串
void append(const char* str)
{
size_t len = strlen(str);//尾插的字串的個數
if (len + _size > _capacity)//原來的字串的個數和要插入的字符的個數大于字串的空間
{
reserve(len + _size);//增容
}
strcpy(_str + _size, str);
_size += len;
}
//運算子+=多載,尾插一個字串
string& operator+=(const char* str)
{
append(str);
return *this;
}
//運算子+=多載,尾插一個string類物件
string& operator+=(const string& s)
{
append(s._str);
return *this;
}
//運算子+=多載,尾插一個字符
string& operator+=(const char ch)
{
push_back(ch);
return *this;
}
//運算子[]多載,訪問字串中的某個字符,既可讀也可以寫,const的string不能呼叫它
char& operator[](size_t i)
{
return _str[i];
}
//運算子[]多載,訪問字串中的某個字符,只能讀不能寫,const呼叫它
const char& operator[](size_t i)const
{
return _str[i];
}
//在某個位置插入一個字符
string& insert(size_t pos,char ch)
{
assert(pos < _size);
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 5 : _capacity * 2;
reserve(newcapacity);
_capacity = newcapacity;
}
//不能這樣實作,pos為0時,end減到-1時,由于end和pos是無符號,所以end會變成最大整數,一直無限回圈
/*size_t end = _size - 1;
while (end >= pos)
{
_str[end - 1] = _str[end];
end--;
}
*/
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* s)
{
assert(pos < _size);
size_t len = strlen(s);
if (len + _size > _capacity)
{
reserve(len + _size);
}
char* end = _str+_size;
while (end >=_str+pos)
{
*(end + len) = *end;
end--;
}
size_t cur = pos;
strncpy(_str + pos, s, len);
_size += len;
return *this;
}
//在某個位置去掉多少個字符
string& erase(size_t pos,size_t n=npos)
{
assert(pos < _size);
size_t leftlen = _size - pos;//leftlen是尾上
if (leftlen <= n)
{
_size = pos;
_str[_size] = '\0';//注意
}
else
{
strcpy(_str + pos, _str + pos + n);
_size -= n;
}
return *this;
}
//從pos位置開始查找查找一個字母
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;
}
}
//找不到回傳npos
return npos;
}
//從某個位置開始,查找一個字串,如果找到,回傳該字串的位置,找不到回傳npos
size_t find(const char* ch, size_t pos = 0)
{
assert(pos < _size);
//strstr(s1,s2)的功能是判斷s2是否為s1的字串,并且回傳s2在s1中的地址,找不到回傳nullptr
const char* ret = strstr(_str+pos, ch);
/*char* cur = _str;
while (cur < _str+_size)
{
if (cur == tmp)
{
return cur - _str;
}
cur++;
}*/
if (ret)
{
return ret - _str;
}
else
{
return npos;
}
return npos;
}
//訪問物件中的字串
char* s_str()const
{
return _str;
}
//判斷字串是否為空
bool empty()const
{
return _size == 0;
}
//清空字串
void clear()
{
_size = 0;
_str[_size] = '\0';
}
};
const size_t string::npos = -1;
//下面這幾個為比較兩個string物件的大小的運算子多載
bool operator==(const sjp::string& s1, const sjp::string& s2)
{
return strcmp(s1.s_str(), s2.s_str()) == 0;
}
bool operator>(const sjp::string& s1, const sjp::string& s2)
{
return strcmp(s1.s_str(), s2.s_str()) > 0;
}
bool operator<(const sjp::string& s1, const sjp::string& s2)
{
return !(s1 == s2) || !(s1 > s2);
}
bool operator>=(const sjp::string& s1, const sjp::string& s2)
{
return !(s1 < s2);
}
bool operator<=(const sjp::string& s1, const sjp::string& s2)
{
return !(s1 > s2);
}
bool operator!=(const sjp::string& s1, const sjp::string& s2)
{
return !(s1 == s2);
}
//運算子<<多載,使cout<<能夠輸出string物件
ostream& operator<<(ostream& out, string& s)
{
for (auto ch: s)//范圍for的使用
{
out << ch;
}
return out;
}
//運算子>>多載使cin>>能夠輸入string物件
istream& operator>>(istream& in, string& s)
{
s.clear();
char ch = in.get();//in.get()能夠獲取空格和回車字符
while (ch != ' ' && ch != '\n')//如果ch為空格或回車,將跳出輸入
{
s += ch;
ch = in.get();
}
return in;
}
}
3.總結
1.strstr(char* s1,char* s2)判斷s2是否為s1的字串,如果是,則回傳該字串在s1中的地址位置,如果不是回傳空,
strcpy(char* s1,char* s2)將s2中的字符拷貝給s1,包括\0;
memset(void* str, ch, size) 從str的位置開始,開辟size個位元組的大小,并將開辟的空間初始為ch
2.寫istream和iostream的多載時,記得回傳的值是istream&或iostream&(注意是參考),引數也是istream&或iostream&
3.寫建構式需要多開辟一個空間給\0,
4.如果沒使用strcpy,洗掉或則增加需要記得在結尾_str[_size]=‘\0’.
模擬實作string常用介面后,我們可以知道string的迭代器的底層是指標,修改資料盡量少用insert,erase,因為它們的使用需要挪動資料,時間復雜度為O(n^2),效率太低了,reserve與resize的區別,
點個贊唄~!!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/290323.html
標籤:其他
上一篇:html的語意化
