String类模拟实现
String类中的成员
private:
size_t _capacity;
size_t _size;
char* _str;
const static size_t npos = -1;
下列函数都为类中的成员函数,访问权限为公有
构造函数
string(const char* str)
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// 现代写法
string(const string& s)
{
string tmp(s._str);
swap(tmp);
}
上面两个构造函数分别是用char*和string对象初始化string对象。第二个函数复用了第一个函数,而swap函数接下来将会提到。
析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
swap函数
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
利用C++库里面的swap函数完成string对象的交换函数。
operator=函数
string& operator=(string s)
{
swap(s);
return *this;
} //现代写法
string& operator=(const string& s)
{
if (this != &s)
{
char* tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
} //传统写法
上面第一个现代写法的原理是参数s是一个临时拷贝对象,通过swap函数将s与当前string对象交换,然后返回当前string对象,由于s是临时变量,出作用域就会自动销毁。
operato[]函数
const char& operator[](size_t pos) const
{
assert(pos <= _size);
return _str[pos];
}
char& operator[](size_t pos)
{
assert(pos <= _size);
return _str[pos];
}
这个函数有两个重载,一个供const对象使用,防止出现权限放大的情况。
reserve函数
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n+1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
reserve函数是扩容函数,若参数大于当前的容量则扩容,若小于等于也不缩容。
puch_back函数
void push_back(char ch)
{
if (_size == _capacity)
{
size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newCapacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
这个函数将一个字符尾插到string对象中
append函数
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
这个函数将一个字符串尾插到string对象中
operator+=函数
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
这个函数有两个重载,一个复用push_back函数尾插字符,一个复用append函数尾插字符串。
insert函数
void insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newCapacity);
}
size_t end = _size+1;
while (end >pos)
{
_str[end] = _str[end-1];
--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);
}
int end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
}
这个函数是将一个字符插入到字符串对应的位置中,或者将一个字符串插入到对应的位置中,设计思想都是将pos后面的字符向后挪,如果是插入字符则挪一位,如果是插入字符串则挪字符串的长度位。
erase函数
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位置开始删除npos个字符,设计思想是将pos+len后面的字符串向前挪到pos位置。
find函数
size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
const char* ptr = strstr(_str+pos, str);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
这个函数是从pos位置开始寻找,返回子串在字符串中出现的第一个位置或者返回字符在字符串中出现的第一个位置。
substr函数
string substr(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
size_t end = pos + len;
if (len == npos || pos + len >= _size)
{
end = _size;
}
string str;
str.reserve(end - pos);
for (size_t i = pos; i < end; i++)
{
str += _str[i];
}
return str;
}
这个函数的作用是从pos位置开始返回长度为len的字串。
clear函数
void string::clear()
{
_size = 0;
_str[0] = '\0';
}
这个函数将字符串逻辑清空
输入输出流函数
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();
char buff[128];
char ch = in.get();
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;
}
输入流函数的设计思想就像是蓄水桶,满了才倒掉,输入够127个字符才去+=,这可以减少不断扩容所带来的消耗。