模拟实现string类,一方面能锻炼自己的代码写作能力,并且能了解到更多的"坑",另一方面能让自己更熟悉string类
需要注意的部分我都写在注释中了,例如string(const string& s)现代写法的坑,以及在调用swap前为什么需要初始化成员变量为0等等
cpp
#include <iostream>
#include <assert.h>
#include<string.h>
using namespace std;
namespace wzn
{
class string
{
public:
//构造函数
//string()
// :_str(new char[1])
//{
// _str[0] = '\0';
//}
string(const char* str = "") // 如果内嵌'\0'需要做额外处理
:_size(strlen(str))
,_capacity(_size)
,_str(new char[_size + 1])
{
memcpy(_str,str,_size + 1);
}
string(const string& s)
:_size(s._size)
,_capacity(s._capacity)
,_str(new char[s._capacity + 1])
{
//strcpy(_str,s._str); //strcpy遇到'\0'会停止,字符串中可能包含空格
memcpy(_str, s._str, _size + 1);
}
/*
string(const string& s)
:_size(0)
,_capacity(0)
,_str(nullptr)
//必须初始化,在该函数里生成的 *this._size 和 *this_capacity 的值是0
//因为=重载调用的时候,*this._size 和 *this_capacity 却没有初始化为0
{
string tmp(s.c_str());
swap(tmp);
}
//现代写法,但是存在问题,因为是c_str()遇到'\0'停止,因此在C++中设计字符串的要考虑是否需要避开c_str等类型的函数。
//假如string str = "hello"
//str += '\0';
//str += "wzn"
//string test(str);
//cout << test << endl; 出来的结果却是hello,'\0'及后面的部分没有拷贝到
*/
/////////////////////////////
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
/////////////////////////////
// 迭代器
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;
}
////////////////////////////
//成员变量操作
const char* c_str() const
{
return _str;
}
const size_t size() const
{
return _size;
}
const size_t capacity() const
{
return _capacity;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
////////////////////////////
//开空间
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1]; // +1是为了存放'\0'
memcpy(tmp, _str, _size + 1);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
////////////////////////////////////
//追加字符/字符串
void push_back(char ch)
{
//扩容
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
void append(const char* str)
{
//扩容
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size + _capacity * 2);
}
memcpy(_str+_size, str, len + 1);
_size += len;
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
void insert(size_t pos, size_t n, const char ch)
{
assert(pos <= _size);
if (_size + n > _capacity)
{
reserve(_size + n + _capacity * 2);
}
//size_t end = _size;
//// 细节点: end != -1
//while (end >= pos && end != -1)
//{
// _str[end + n] = _str[end];
// --end;
//}
//for (int i = 0; i < n; ++i)
//{
// _str[pos + i] = ch;
//}
// 上面的部分是方便逻辑理解,实际使用memmove效率更高,不使用memcpy是因为内存重叠
memmove(_str + pos + n, _str + pos, _size - pos);
memset(_str + pos, ch, n);
_size += n;
}
void insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size + _capacity * 2);
}
//size_t end = _size;
//while (end >= pos && end != -1)
//{
// _str[end + len] = _str[end];
// --end;
//}
//for (int i = 0; i < len; ++i)
//{
// _str[pos + i] = str[i];
//}
// 上面的部分是方便逻辑理解,实际使用memmove效率更高,不使用memcpy是因为内存重叠
memmove(_str + pos + len, _str + pos, _size - pos);
memcpy(_str + pos, str, len);
_size += len;
_str[_size] = '\0';
}
/////////////////////////////////
//删除字符
void erase(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
if (len >= _size - pos)
{
// 情况1:删除长度超过剩余字符 → 直接截断到 pos 位置
_size = pos;
}
else
{
// 情况2:删除指定长度 → 后续字符向前覆盖
size_t start = pos + len;
//while (start < _size)
//{
// _str[start - len] = _str[start];
// ++start;
//}
// 上面的部分是方便逻辑理解,实际使用memmove效率更高,不使用memcpy是因为内存重叠
memmove(_str + pos, _str + start, _size - start);
_size -= len;
}
_str[_size] = '\0';
}
////////////////////////////////////
//查找字符
size_t find(char ch, size_t pos = 0)
{
assert(pos < _size);
for (int i = pos; i < _size; ++i)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
assert(pos < _size);
const char* ptr = strstr(_str + pos, str);
if (ptr)
{
return ptr - _str;
}
else
{
return npos;
}
}
//////////////////////////////////
// 截取字符段
string substr(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
size_t n = len;
if (len > _size - pos)
{
n = _size - pos;
}
string tmp;
tmp.reserve(n);
//for (size_t i = pos; i < pos + n; ++i)
//{
// tmp += _str[i];
//}
// for循环+=的方式为逻辑理解,效率太低,实际使用memcpy效率更高
memcpy(tmp._str, _str + pos, n);
// 更新 tmp 的长度 + 补 \0
tmp._size = n;
tmp._str[tmp._size] = '\0';
return tmp;
}
///////////////////////////////
// 清除字符串
void clear()
{
_str[0] = '\0';
_size = 0;
}
/////////////////////////////
// 开空间
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
reserve(n);
//for (int i = _size; i < n; ++i)
//{
// _str[i] = ch;
//}
// 上面的部分是方便逻辑理解,实际使用memset效率更高
memset(_str + _size, ch, n - _size);
}
_size = n;
_str[_size] = '\0';
}
////////////////////////////
// 交换
void swap(string& s)
{
if (this != &s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
}
////////////////////////////
// 运算符重载
string& operator=(string tmp)
{
//if (this != &s)
//{
//string tmp(s);
//std::swap(_str,tmp._str);
//std::swap(_size,tmp._size);
//std::swap(_capacity,tmp._capacity);
//}
swap(tmp);
return *this;
}
//bool operator<(const string& s)
//{
// size_t len1 = 0;
// size_t len2 = 0;
// while (len1 < _size && len2 < s._size)
// {
// if (_str[len1] < s._str[len2])
// {
// return true;
// }
// else if (_str[len1] > s._str[len2])
// {
// return false;
// }
// len1++;
// len2++;
// }
// return _size < s._size;
//}
bool operator<(const string& s) const
{
//不用strcmp是因为字符串中可能包含'\0' ,C语言的str系列遇到'\0'则停止
int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
return ret == 0 ? _size < s._size : ret < 0;
}
bool operator==(const string& s) const
{
return _size == s._size && memcmp(_str, s._str, _size) == 0;
}
bool operator<=(const string& s) const
{
return *this < s || *this == s;
}
bool operator>(const string& s) const
{
return !(*this <= s);
}
bool operator>=(const string& s) const
{
return !(*this < s);
}
bool operator!=(const string& s) const
{
return !(*this == s);
}
private:
size_t _size = 0;
size_t _capacity = 0;
char* _str;
public:
static const size_t npos;
};
const size_t string::npos = -1;
//////////////////////////
// 因为并没有访问类的成员变量,因此不需要声明友元
// 运算符重载流插入
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
//////////////////////////
// 运算符重载流提取
istream& operator>>(istream& in, string& s)
{
// 先clear!!!
s.clear();
char ch = in.get();
// 处理前缓冲区前面的空格或者换行
while (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
{
ch = in.get();
}
char buff[128];
int i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
};