文章目录
以utf-8编码的string进行模拟,之后还会持续维护和新增内容,有什么问题请大家及时指出。
1.string的成员变量
private:
char* _str = nullptr;
//以下两参数不包含'\0'
size_t _size = 0;
size_t _capacity = 0;
public:
static const int npos;
2.构造、析构、拷贝构造、operator=()
//构造函数
string(const char* str ="")
:_size(strlen(str))
{
//这样初始化可以减少strlen的调用
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//s2(s1)
//深拷贝
//string(const string& s)
//{
// _str = new char[s._capacity + 1];
// strcpy(_str, s._str);
// _size = s._size;
// _capacity = s._capacity;
//}
//拷贝现代写法
//这里不能写成和operator=一样,因为传参需要调用拷贝构造
//而这里就是实现拷贝构造,写成那样会死循环
string(const string& s)
{
//调用构造
string tmp(s._str);
swap(tmp);
}
//string& operator=(const string& s)
//{
// char* tmp = new char[s._capacity + 1];
// strcpy(tmp, s._str);
// delete[] _str;
// _str = tmp;
// _size = s._size;
// _capacity = s._capacity;
// return *this;
//}
//现代写法
string& operator=(string s)
{
swap(s);
return *this;
}
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
3.c_str()、swap()、clear()
//返回一个指向正规C字符串的指针常量, 内容与本string串相同
//为了与c语言兼容,在c语言中没有string类型,
//故必须通过string类对象的成员函数c_str()
//把string 对象转换成c中的字符串样式
const char* c_str() const{
return _str;
}
//交换
//使用模板的swap调用三次拷贝和一次析构,效率低
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
void clear()
{
_size = 0;
_str[_size] = '\0';
}
4.size()、capacity()、operator[]()
size_t size() const{
return _size;
}
size_t capacity() const{
return _capacity;
}
char& operator[](size_t pos)
{
//检查越界,相较于数组的优势
assert(pos < _size);
//_str所在的区域在堆上,出了作用域还在
//用引用返回返回的不是拷贝,是别名
return _str[pos];
}
//为了const string加的函数重载
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
5.迭代器iterator
typedef char* iterator;
typedef const char* const_iterator;
const_iterator begin() const{
return _str;
}
const_iterator end() const{
return _str + _size;
}
iterator begin() {
return _str;
}
iterator end(){
return _str + _size;
}
6.reserve()、resize()
reserve用来开辟空间,resize用来指定容器大小
//开辟空间,若n<_capacity,则保持不变
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//多开一个,存 \0
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
//如果指定的大小大于原来的大小,扩充出来的空间全部置为ch
void resize(size_t n,char ch = '\0')
{
if (n <= _size)
{
_str[n] = '\0';
_size = n;
}
else {
reserve(n);
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_str[n] = '\0';
_size = n;
}
}
7.push_back(),append(),operator+=()
void push_back(char ch)
{
//扩容2倍
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
//复用insert()
//insert(_size, ch);
}
void append(const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(_size + len);
}
//strcat先要从头遍历到'\0',才在后面追加
//strcpy会把'\0'也拷贝过去
strcpy(_str + _size, str);
_size += len;
//复用insert()
//insert(_size, str);
}
string& operator+= (char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
8.insert()、erase()
void insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
int end = _size;
//pos==0的时候由于end=-1,pos类型为size_t
//end强转为size_t类型,导致死循环,所以pos要强转
while (end >= (int)pos){
_str[end + 1] = _str[end];
--end;
}
_str[pos] = ch;
_size++;
}
void 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 - 1)
{
_str[end] = _str[end - len];
end--;
}
//char *strncpy(char *dest, const char *src, size_t n)
//把 src 所指向的字符串复制到 dest,最多复制 n 个字符。
//当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
strncpy(_str + pos, str, len);
_size += len;
}
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
//len > _size - pos 解决溢出风险
if (len == npos || len > _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else {
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
9.find()、substr()
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;
}
size_t find(const char* sub, size_t pos = 0) const {
assert(pos < _size);
const char* p = strstr(_str + pos, sub);
if (p)
{
return p - _str;
}
else
{
return npos;
}
}
string substr(size_t pos = 0, size_t len = npos)
{
string sub;
if (len == npos || len >= _size-pos)
{
for (size_t i = pos; i < _size ; i++)
{
sub += _str[i];
}
}
else
{
for (size_t i = pos; i < pos + len; i++)
{
sub += _str[i];
}
}
return sub;
}
10.比较运算符重载
bool operator==(const string& s1, const string& s2)
{
int ret = strcmp(s1.c_str(), s2.c_str());
return ret == 0;
}
bool operator>(const string& s1, const string& s2)
{
int ret = strcmp(s1.c_str(), s2.c_str());
return ret > 0;
}
bool operator<(const string& s1, const string& s2)
{
int ret = strcmp(s1.c_str(), s2.c_str());
return ret < 0;
}
bool operator>=(const string& s1, const string& s2)
{
return s1 > s2 || s1 == s2;
}
bool operator<=(const string& s1, const string& s2)
{
return s1 < s2 || s1 == s2;
}
bool operator!=(const string& s1, const string& s2)
{
return !(s1 == s2);
}
11.operator<<(),operator>>()、getline()
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;
ch=in.get();
//使用字符数组存储,可以避免频繁扩容,效率低
char buff[128];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[127] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
std::istream& getline(std::istream& in, string& s)
{
s.clear();
char ch;
ch = in.get();
char buff[128];
size_t i = 0;
while (ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[127] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
}
12.总代码
class string {
public:
//迭代器
typedef char* iterator;
typedef const char* const_iterator;
const_iterator begin() const{
return _str;
}
const_iterator end() const{
return _str + _size;
}
iterator begin() {
return _str;
}
iterator end(){
return _str + _size;
}
//string()
// :_str(new char[1])
// ,_size(0)
// ,_capacity(0)
//{
// _str[0] = '\0';
//}
//初始化列表是按声明顺序进行的
//全缺省构造函数
string(const char* str ="")
:_size(strlen(str))
{
//这样初始化可以减少strlen的调用
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//拷贝构造函数:它用于在创建一个新对象时,将一个已有对象的值复制到新对象中。
//通常在对象的初始化过程中调用,如对象声明时的初始化、函数参数传递(按值传递)或返回对象(按值返回)等情况。
//赋值运算符重载函数:它用于将一个已有对象的值赋给另一个已有对象。
//通常在已存在的对象上调用,使用赋值运算符
//s2(s1)
//深拷贝
//string(const string& s)
//{
// _str = new char[s._capacity + 1];
// strcpy(_str, s._str);
// _size = s._size;
// _capacity = s._capacity;
//}
//拷贝现代写法
//这里不能写成和operator=一样,因为传参需要调用拷贝构造
//而这里就是实现拷贝构造,写成那样会死循环
string(const string& s)
{
//调用构造
string tmp(s._str);
swap(tmp);
}
//string& operator=(const string& s)
//{
// char* tmp = new char[s._capacity + 1];
// strcpy(tmp, s._str);
// delete[] _str;
// _str = tmp;
// _size = s._size;
// _capacity = s._capacity;
// return *this;
//}
//现代写法
string& operator=(string s)
{
swap(s);
return *this;
}
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//返回一个指向正规C字符串的指针常量, 内容与本string串相同
//为了与c语言兼容,在c语言中没有string类型,
//故必须通过string类对象的成员函数c_str()
//把string 对象转换成c中的字符串样式
const char* c_str() const{
return _str;
}
//遍历
//将const修饰的“成员函数”称之为const成员函数,
// const修饰类成员函数,实际修饰该成员函数隐含的this指针,
//表明在该成员函数中不能对类的任何成员进行修改
//返回string的大小
size_t size() const{
return _size;
}
size_t capacity() const{
return _capacity;
}
char& operator[](size_t pos)
{
//检查越界,相较于数组的优势
assert(pos < _size);
//_str所在的区域在堆上,出了作用域还在
//用引用返回返回的不是拷贝,是别名
return _str[pos];
}
//为了const string加的函数重载
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
//如果指定的大小大于原来的大小,扩充出来的空间全部置为ch
void resize(size_t n,char ch = '\0')
{
if (n <= _size)
{
_str[n] = '\0';
_size = n;
}
else {
reserve(n);
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_str[n] = '\0';
_size = n;
}
}
//开辟空间,若n<_capacity,则保持不变
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//多开一个,存 \0
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
//扩容2倍
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
//使用insert()
//insert(_size, ch);
}
void append(const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(_size + len);
}
//strcat先要从头遍历到'\0',才在后面追加
//strcpy会把'\0'也拷贝过去
strcpy(_str + _size, str);
_size += len;
//使用insert()
//insert(_size, str);
}
string& operator+= (char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
//插入单个字符
void insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
int end = _size;
//pos==0的时候由于end=-1,pos类型为size_t
//end强转为size_t类型,导致死循环,所以pos要强转
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
--end;
}
_str[pos] = ch;
_size++;
}
//在某个位置插入字符串
void 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 - 1)
{
_str[end] = _str[end - len];
end--;
}
//char *strncpy(char *dest, const char *src, size_t n)
//把 src 所指向的字符串复制到 dest,最多复制 n 个字符。
//当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
strncpy(_str + pos, str, len);
_size += len;
}
//删除某个位置的字符
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
//len > _size - pos 解决溢出风险
if (len == npos || len > _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else {
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
//交换
//使用模板的swap调用三次拷贝和一次析构,效率低
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
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;
}
size_t find(const char* sub, size_t pos = 0) const {
assert(pos < _size);
const char* p = strstr(_str + pos, sub);
if (p)
{
return p - _str;
}
else
{
return npos;
}
}
string substr(size_t pos = 0, size_t len = npos)
{
string sub;
if (len == npos || len >= _size-pos)
{
for (size_t i = pos; i < _size ; i++)
{
sub += _str[i];
}
}
else
{
for (size_t i = pos; i < pos + len; i++)
{
sub += _str[i];
}
}
return sub;
}
//将容器清空
void clear()
{
_size = 0;
_str[_size] = '\0';
}
private:
char* _str = nullptr;
//以下两参数不包含'\0'
size_t _size = 0;
size_t _capacity = 0;
public:
static const int npos;
};
const int string::npos = -1;
//重载swap
void swap(string& x, string& y)
{
x.swap(y);
}
bool operator==(const string& s1, const string& s2)
{
int ret = strcmp(s1.c_str(), s2.c_str());
return ret == 0;
}
bool operator>(const string& s1, const string& s2)
{
int ret = strcmp(s1.c_str(), s2.c_str());
return ret > 0;
}
bool operator<(const string& s1, const string& s2)
{
int ret = strcmp(s1.c_str(), s2.c_str());
return ret < 0;
}
bool operator>=(const string& s1, const string& s2)
{
return s1 > s2 || s1 == s2;
}
bool operator<=(const string& s1, const string& s2)
{
return s1 < s2 || s1 == s2;
}
bool operator!=(const string& s1, const string& s2)
{
return !(s1 == s2);
}
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;
ch=in.get();
//使用字符数组存储,可以避免频繁扩容,效率低
char buff[128];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[127] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
std::istream& getline(std::istream& in, string& s)
{
s.clear();
char ch;
ch = in.get();
char buff[128];
size_t i = 0;
while (ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[127] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
}
1358





