基础认识:
- string严格意义上不属于STL容器,其出现的时间早于STL,这也导致了string官方库中部分函数冗余。但是这也体现了语言发展中的一个重要现象——向前兼容。
- 和很多STL容器一样,string是一个类,核心是一个顺序表,同时提供一系列接口,以及方法,从而简化在编程中对字符串的相关操作。
- 和其他容器不同的是,库中string类使用模板的意义不在于,能使不同内置类型得到相应的实例化,而是针对不同文字的编码,常见的有:uicode(万国码)、UTF-8、UTF-16、UTF-32(数字越大能表示的字符数量就越多)、gbk。
- string类中的reserve当n小于capacity时不一定会缩容,如VS上不会,但linux上会。
- 谨慎使用insert头插、中间插,replace(如果是平替还好),erase中间删且量小,因为会扩容和移动数据,效率低下。
三种重要的遍历方式
方括号遍历:
比较常见的一种遍历,因为string支持方括号的重载
迭代器遍历:
因为string特殊的物理结构,可以简单地认为,迭代器就是指针。而实际上迭代器是一种封装的思想,使用者不用花太多心思在如何根据需要访问数据上,只需要延续使用指针的传统,从而达到了简化的作用。begin()、end()一组左闭右开的区间,end()指向的是最后一个字符的下一个位置。
范围for遍历(特殊):
范围for遍历的底层其实还是迭代器,在模拟实现中,实现好迭代器后,范围for的写法立即生效。同时注意不用对ch++或者解引用。
模拟实现
不考虑编码、模板,旨在体会string构造的模拟练习:
实现中的要点回顾
- 在做全缺省传参的时候,缺省值不要给nullptr,应该给'\0',并且斜杠零不应该计入capacity与size中。
- strcpy会把'\0'也拷到目标中,先拷贝判断。
- reserve开空间要多开一个留给斜杆零
- 拷贝、修改字符串的时候一定注意有没有考虑到斜杠零
-
+=运算符重载时reserve不再以二倍来扩容,因为要插入的字符串太大时,两倍有可能达不到。
- insert是实现时,size_t类型,在和0的比较会出现问题
- 编译器规定const int/size_t类型可以直接在申明中定义,注意只能是这两种。
- substr当能超出时记得更新
代码模板回顾:
.cpp
#include"string.h"
namespace Solution
{
const size_t string::npos = -1;
void string::reserve(size_t n)
{
if (n > _capacity)
{
//cout << "reserve:" << n << endl;
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void string::push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
void string::append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
// 大于2倍,需要多少开多少,小于2倍按2倍扩
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
strcpy(_str + _size, str);
_size += len;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
// 挪动数据
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* s)
{
assert(pos <= _size);
size_t len = strlen(s);
if (_size + len > _capacity)
{
// 大于2倍,需要多少开多少,小于2倍按2倍扩
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
size_t end = _size + len;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = s[i];
}
_size += len;
}
void string::erase(size_t pos, size_t len)
{
assert(pos < _size);
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
for (size_t i = pos + len; i <= _size; i++)
{
_str[i - len] = _str[i];
}
_size -= len;
}
}
size_t string::find(char ch, size_t pos)
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t string::find(const char* str, size_t pos)
{
assert(pos < _size);
const char* ptr = strstr(_str + pos, str);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
string string::substr(size_t pos, size_t len)
{
assert(pos < _size);
// len大于剩余字符长度,更新一下len
if (len > _size - pos)
{
len = _size - pos;
}
string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;
}
bool operator<(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) < 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);
}
bool operator>=(const string& s1, const string& s2)
{
return !(s1 < s2);
}
bool operator==(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) == 0;
}
bool operator!=(const string& s1, const string& s2)
{
return !(s1 == s2);
}
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
// 17:16继续
istream& operator>>(istream& in, string& s)
{
s.clear();
const int N = 256;
char buff[N];
int i = 0;
char ch;
//in >> ch;
ch = in.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == N-1)
{
buff[i] = '\0';
s += buff;
i = 0;
}
//in >> ch;
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
}
.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;
namespace Solution
{
class string
{
public:
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;
}
/*string()
:_str(new char[1]{'\0'})
,_size(0)
,_capacity(0)
{}*/
// 短小频繁调用的函数,可以直接定义到类里面,默认是inline
string(const char* str = "")
{
_size = strlen(str);
// _capacity不包含\0
_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;
}
// s2 = s1
// s1 = s1
string& operator=(const string& s)
{
if (this != &s)
{
delete[] _str;
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
const char* c_str() const
{
return _str;
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
size_t size() const
{
return _size;
}
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);
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
void insert(size_t pos, char ch);
void 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);
size_t find(const char* str, size_t pos = 0);
string substr(size_t pos = 0, size_t len = npos);
private:
//char _buff[16];
char* _str;
size_t _size;
size_t _capacity;
//static const size_t npos = -1;
static const size_t npos;
/*static const int N = 10;
int buff[N];*/
};
bool operator<(const string& s1, const string& s2);
bool operator<=(const string& s1, const string& s2);
bool operator>(const string& s1, const string& s2);
bool operator>=(const string& s1, const string& s2);
bool operator==(const string& s1, const string& s2);
bool operator!=(const string& s1, const string& s2);
ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);
}