string模拟实现
公有及私有成员预览
#pragma once
using namespace std;
namespace carl
{
class string
{
public:
//迭代器相关
typedef char* iterator;
typedef const char* const_iterator;
iterator begin();
iterator end();
const iterator begin()const;
const iterator end()const;
//默认成员
string(const char* str = "");
string(const string& s);
~string();
string& operator=(const string& s);
//容量相关
size_t size()const;
size_t capacity()const;
void reserve(size_t n);
void resize(size_t n, char ch='\0');
void clear();
//访问及遍历相关
const char& operator[](size_t pos) const;
char& operator[](size_t pos);
//修改相关
void push_back(char ch);
const char* c_str();
void append(const char* str);
void append(const string& s);
string& operator+=(const char* str);
string& operator+=(const char ch);
string& operator+=(const string& s);
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char* str);
void erase(size_t pos,size_t len=npos);
size_t find(char ch, size_t pos = 0)const;
size_t find(const char* str, size_t pos = 0)const;
string substr(size_t pos, size_t len = npos)const;
void swap(string& temp);
//重载运算符相关
bool operator>(string& s)const;
bool operator==(string& s)const;
bool operator>=(string& s)const;
bool operator<(string& s)const;
bool operator<=(string& s)const;
bool operator!=(string& s)const;
private:
size_t _size;
size_t _capacity;
char* _str;
public:
const static size_t npos=-1 ;
};
//流提取和流插入
ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);
}
默认成员函数
我们只自模拟实现四个默认成员函数:构造,拷贝构造,析构,赋值运算符重载
其中:拷贝构造,赋值运算符重载,我们有两种写法:传统写法和现代写法。
现代写法相对于传统写法来说效率较高且较简洁,而传统写法可读性较高。
我们首先给出这四个默认成员函数的传统写法(现代写法需要依赖复用构造函数)
传统写法
构造函数
//默认成员(传统)
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity+1] ;
strcpy(_str, str);
}
析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
拷贝构造
string(const string& s)
:_str(new char[s._capacity + 1])
,_size(s._size)
,_capacity(s._capacity)
{
strcpy(_str, s._str);
}
赋值运算符重载
string& operator=(const string& s)
{
if (this != &s)
{
char* temp = new char[s._capacity + 1];
strcpy(temp, s._str);
delete[] _str;
_str = temp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
现代写法
拷贝构造
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
string tmp(s._str);
swap(tmp);
}
赋值运算符重载
string& operator=(string s)
{
swap(s);
return *this;
}
区别
简析传统写法和现代写法的区别:
传统拷贝构造思路
现代拷贝构造思路:复用了构造函数,利用了swap函数
void swap(string& tmp) { ::swap(_str, tmp._str); ::swap(_size, tmp._size); ::swap(_capacity, tmp._capacity); }
传统赋值
现代赋值:调用拷贝构造函数以实参为模板拷贝形参,再利用swap函数将目标主体与形参交换,出了函数作用域后形参会自动销毁
容量相关
直接给出代码自模拟实现代码:
size_t size()const |
---|
size_t capacity()const |
void reserve(size_t n) |
void resize(size_t n, char ch = ‘\0’) |
void clear() |
//容量相关
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
void reserve(size_t n)
{
if(n>_capacity)
{
char* temp = new char[n+1];
strcpy(temp, _str);
delete[] _str;
_str = temp;
_capacity = n;
}
//n<capacity什么都不用做
}
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
reserve(n);
for (size_t i = _size;i < n;i++)
{
_str[i] = ch;
}
_str[n] = '\0';
_size = n;
}
else
{
_str[n] = '\0';
_size = n;
}
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
访问及遍历相关
const char& operator[](size_t pos) const; |
---|
char& operator[](size_t pos); |
//访问及遍历相关
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
修改相关
void push_back(char ch)
const char c_str()const*
void append(const char str);*
void append(const string& s);
string& operator+=(const char str);*
string& operator+=(const char ch);
string& operator+=(const string& s);
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char str);*
void erase(size_t pos,size_t len=npos);
size_t find(char ch, size_t pos = 0)const;
size_t find(const char str, size_t pos = 0)const;*
string substr(size_t pos, size_t len = npos)const;
//修改相关
//尾插
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
//返回c语言风格字符串
const char* c_str()const
{
return _str;
}
//追加一个字符串
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size = _size + len;
}
//追加一个string类型
void append(const string& s)
{
append(s._str);
}
//+=字符串
string& operator+=(const char* str)
{
append(str);
return *this;
}
//+=字符
string& operator+=(const char ch)
{
push_back(ch);
return *this;
}
//+=string类型
string& operator+=(const string& s)
{
append(s._str);
return *this;
}
//在pos位置插入一个字符
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
size_t end = _size+1;
while (end > pos)
{
_str[end] = _str[end-- - 1];
}
_str[pos] = ch;
_size++;
_str[_size+1]='\0';
return *this;
}
//在pos位置插入一个字符串
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 end = _size + len;
while (end > pos + len)
{
_str[end] = _str[end-- - 3];
}
strncpy(_str + pos, str, len);
_size += len;
_str[_size + 1] = '\0';
return *this;
}
//删除pos位置开始长度为len的字符串
void erase(size_t pos, size_t len = npos)
{
assert(pos <= _size);
if (len == npos || pos + len > _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
//在pos位置开始找一个字符并返回其下标
size_t find(char ch, size_t pos = 0)const
{
assert(pos <= _size);
for (size_t i = pos;i < _size;i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
//在pos位置开始找到一个字符串并返回该字符串开始的下标
size_t find(const char* str, size_t pos = 0)const
{
assert(str);
assert(pos <= _size);
const char* ptr = strstr(_str + pos, str);
if (ptr == nullptr)
{
return npos;
}
else return ptr - _str;
}
//截取pos位置之后的长度为n的字符串
string substr(size_t pos, size_t len = npos)const
{
assert(pos < _size);
size_t RLen = len;
if (len == npos || pos + len > _size)
{
RLen = _size - pos;
}
string tmp;
for (size_t i = pos;i < RLen;i++)
{
tmp += _str[i];
}
return tmp;
}
关于insert给出动图演示:
重载运算符相关
实现两个,其他复用
//重载运算符相关
bool operator>(string& s)const
{
return strcmp(_str, s._str)>0;
}
bool operator==(string& s)const
{
return strcmp(_str, s._str) == 0;
}
bool operator>=(string& s)const
{
return *this > s || *this == s;
}
bool operator<(string& s)const
{
return !(*this >= s);
}
bool operator<=(string& s)const
{
return!(*this > s);
}
bool operator!=(string& s)const
{
return !(*this == s);
}
流提取和流插入重载
//流提取
ostream& operator<<(ostream& out, const string& s)
{
for (size_t i = 0;i < s.size();i++)
{
out << s[i] ;
}
return out;
}
//流插入
istream& operator>>(istream& in, string& s)
{
s.clear();
char ch;
ch = in.get();
//数组出了作用域会销毁,因此先放进数组里,再+=到string中,这样方便且能避免过长而扩容太多的情况
const size_t N = 32;
char buff[N];
size_t i= 0;
while (ch != ' ' || ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
buff[i] = '\0';
s += buff;
return in;
}
//getline
istream& getline(istream& in,string& s)
{
s.clear();
char ch = in.get();
while (ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
}
迭代器相关
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const iterator begin()const
{
return _str;
}
const iterator end()const
{
return _str + _size;
}