==reserve预留空间==
在开始字符串增删查改之前,有必要介绍这个函数,使用它可以更好的控制我们string对象的内存管理。
void string::reserve(size_t n)
{
if (_capacity < n) {
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
当n大于当前容量_capacity的时候,进行空间的开辟,将_str中的内容拷贝到新空间中去,同时delete释放旧空间。
==尾插字符和字符串==
接口函数,一个是push_back,一个是append。
void string::push_back(char ch)
{
if (_size >= _capacity) {
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
void string::append(const char* str)
{
size_t len = strlen(str);
if (_capacity < _size + len) {
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
这两个成员函数都使用了reserve来预留空间。
同时还需要有 运算符重载+= 来实现尾插,用重载的运算符执行这样的操作才是最爽的。
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
其实不用再实现一遍了,直接复用就行。
可以简单看一下使用的效果:
string str("hello world!");
char ch = 'T';
const char* s = "hhhhhh";
cout << str << endl;
str += ch;
cout << str << endl;
str += s;
cout << str << endl;
==字符或字符串的插入和删除==
这里的逻辑稍微有些复杂,而且还有几个比较容易掉的坑。
// 字符的插入
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size >= _capacity) {
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
// 注意这里为什么要+1,size_t是无符号整型,没有负值
//当有符号和无符号比较时,统一会被转成无符号
size_t end = _size + 1;
while (end > pos) {
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
}
// 字符串的插入
void string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len == 0)return;
if (_size + len > _capacity) {
reserve(_size + len);
}
// 注意这里为什么要+len,size_t是无符号整型,没有负值
//当有符号和无符号比较时,统一会被转成无符号
size_t end = _size + len;
while (end > pos + len - 1) {
_str[end] = _str[end - len];
--end;
}
memcpy(_str + pos, str, len);
_size += len;
}
// 字符串的删除
void string::erase(size_t pos, size_t len)
{
assert(pos < _size);
// 当pos+len过大,超过_size,则取到末尾
if (len >= _size - pos) {
_str[pos] = '\0';
_size = pos;
}
else {
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
使用案例:
string str("hello world!");
char ch = 'T';
const char* s = "hhhhhh";
cout << str << endl;
str.insert(3, ch);
cout << str << endl;
str.insert(3, s);
cout << str << endl;
str.erase(3, strlen(s));
cout << str << endl;
==find查找==
查找C语言查找字符串的库也有提供,可以直接使用。
size_t string::find(char ch, size_t pos)
{
for (size_t i = pos; i < _size; i++) {
if (_str[i] == ch)
return i;
}
return npos;
}
size_t string::find(const char* sub, size_t pos)
{
const char* ptr = strstr(_str, sub);
return ptr - _str;
}
使用案例:
string str("hello world!");
cout << str.find('w') << endl;
cout << str.find("wor") << endl;
缺省参数在函数声明那里。
==比大小运算符重载==
C语言中,有一个按能按字典序将字符串比大小的函数——strcmp
。
bool string::operator>(const string& str) const
{
return strcmp(_str, str._str) > 0;
}
bool string::operator==(const string& str) const
{
return strcmp(_str, str._str) == 0;
}
bool string::operator>=(const string& str) const
{
return *this > str || *this == str;
}
bool string::operator<(const string& str) const
{
return !(*this >= str);
}
bool string::operator<=(const string& str) const
{
return !(*this > str);
}
bool string::operator!=(const string& str) const
{
return !(*this == str);
}
只需要实现前两个,后面的复用就行。
==获取子串==
使用substr可以获取所需对象字串。
string string::substr(size_t pos, size_t len)
{
if (pos + len >= _size) {
string sub(_str + pos);
return sub;
}
else {
string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++) {
sub += _str[pos + i];
}
return sub;
}
}
==清除clear==
可以将对象内容都删除,但_capacity保持不变。
void string::clear()
{
_str[0] = '\0';
_size = 0;
}
==流插入和流提取==
这两个函数之前在Date类部分实现的时候,定义为了Date类的友元。但是,流插入和流提取其实可以不定义为string类的友元。
// 流插入
std::ostream& operator<<(std::ostream& os, const string& str)
{
for (int i = 0; i < str.size(); i++)
os << str[i];
return os;
}
// 流提取
std::istream& operator>>(std::istream& is, string& str)
{
str.clear();
char ch = is.get();
while (ch != ' ' && ch != '\n') {
str += ch;
ch = is.get();
}
return is;
}
string对象读取的截断是空格和换行,但是cin
对象默认是读不到这两个字符的,这就需要使用另一种读取方式:cin.get()
。
使用案例:
string str;
cin >> str;
cout << str << endl;
==常量成员npos==
常量成员的定义需要实现在类的外部,像这样:
const size_t string::npos = -1;
当常量的内容是整型(浮点型和字符类型等都不行)时,也可以作为缺省参数进行定义。
结语
本篇博客主要介绍了string类常用接口的实现,包括默认成员函数,迭代器,字符和字符串的插入删除等等内容。
后续博主还会继续分享与STL相关的内容,感谢大家的支持。♥