string的核心接口简介
也就是增删查改
先来个框架
我给的架子比较现代化,我还是比较建议注释的传统部分,不容易出错。
namespace chen
{
class string
{
public:
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_size + 1]; //后一个位置给‘\0’
strcpy(_str, str);
}
//s1(s2)
/*string(const string& s)
{
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}*/
string(const string& s)
{
_str = nullptr; //必须给空,否则程序会崩溃掉
string tmp(s._str);
swap(tmp); //s1.swap(tmp)
}
//s1 = s2
/* string& operator=(const string& s)
{
delete[] _str;
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
return *this;
}*/
string& operator=(string s) //临时对象,一份拷贝
{
swap(s);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
}
char* c_str()
{
return _str;
}
void swap(string& s)
{
::swap(_str, s._str); //::域操作符,访问编译器写好的全局swap函数,交换的是指针的值(类似于浅拷贝)
::swap(_capacity, s._capacity);
::swap(_size, s._size);
}
private:
char* _str;
size_t _capacity;
size_t _size;
static const size_t npos;
};
const size_t string::npos = -1;
}
operator[ ]重载 和 迭代器iterator 访问
迭代器就是类似于指针的东西,不用想的太过于复杂
typedef char* iterator;
typedef const char* const_iterator; //const迭代器,只允许读
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator cbegin() const
{
return _str;
}
const_iterator cend() const
{
return _str + _size;
}
char& operator[](size_t pos) //这里引用返回,表示可读可写
{
return _str[pos];
}
关于空间扩容reserve和resize
1,void reserve (size_t n = 0); ,扩容,如果n小于目前已有的空间,什么事情都不做。
扩容的时候不一定会扩容到指定的空间大小,因为编译器有默认的扩容机制(VS是扩2倍)
2,void resize (size_t n, char c); 调整有效字符个数,n大于有效字符个数,则填充变量c,
c默认是‘\0’(可能发生扩容),如果小于,则删除元素,到n个。
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
//strcpy(tmp, _str);为什么不用strcpy呢?
strncpy(tmp, _str,_size+1); //记得拷贝\0
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char c = '\0')
{
if (n < _size)
{
_str[n] = '\0';
_size = n;
}
else
{
if (n > _capacity)
reserve(n);
for (int i = _size; i < n; i++)
_str[i] = c;
_str[n] = '\0';
_size = n;
}
}
追加函数append() ,+= ,insert()
要学会代码的复用(偷懒原则)。写一个insert(),其他的基本上都ok了
//pos的位置插入字符串
string& insert(size_t pos,const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len+_size > _capacity)
{
reserve(len+_size);
}
//1,挪动数据,从后向前
int end = _size; //为什么是int? size_t 为-1就是int_max了
while (end >= (int)pos) //为什么要强转? 会有隐式类型转换的问题
{
_str[end + len] = _str[end];
end--;
}
strncpy(_str + pos, str, len);
_size = len + _size;
//这里用指针的方式挪动,或则起始位置pos在\0后一个位置也可以避免
return *this;
}
//在pos的位置插入n个字符
string& insert(size_t pos, size_t n, char c)
{
int len = n + _size;
if (len > _capacity)
{
reserve(len);
}
//挪动数据,空出来n个空间
int end = _size;
while (end >= (int)pos)
{
_str[end + n] = _str[end];
end--;
}
//填数据
while (n)
{
n--;
_str[pos] = c;
pos++;
}
_size = len;
return *this;
}
string& append(const char* s)
{
insert(_size, s);
return *this;
}
string& append(size_t n, char c)
{
insert(_size, n, c);
return *this;
}
string& operator+= (const string& str)
{
insert(_size, str._str);
return *this;
}
string& operator+= (const char* s)
{
insert(_size, s);
return *this;
}
void push_back(char c)
{
insert(_size, 1, c);
}
删除的接口erase()
//在pos的位置向后删除len个字符
string& erase(size_t pos = 0, size_t len = npos)
{
//1,后面的元素不够删,直接在pos的位置给\0
//2,后面的元素比较多,需要挪动删除,从前向后
if (_size - pos < len)
{
_str[pos] = '\0';
_size = pos;
}
else
{
int end = pos + len;
while (end <= (int)_size)
{
_str[end - len] = _str[end];
end++;
}
_size = _size - len;
}
return *this;
}
//尾删
void pop_back()
{
erase(_size - 1, 1);
}
查找函数find的接口
//从pos的位置向后找子字符串
size_t find(const char* s, size_t pos = 0) const
{
assert(pos < _size);
char* ret = strstr(_str + pos, s);
if (ret == nullptr)
{
return npos;
}
else
{
return ret - _str; //指针减指针是元素的个数,恰好是那个找到的下标位置
}
}
//从pos的位置找一个字符
size_t find(char c, size_t pos = 0) const
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == c)
return i;
}
return npos;
}
来一波轻松愉快的比较运算符的重载
因为比较简单也懒得动脑子代码复用了
inline bool operator>(const string& s1, const string& s2)
{
return (strcmp(s1.c_str(), s2.c_str()) > 0);
}
inline bool operator>=(const string& s1, const string& s2)
{
return (strcmp(s1.c_str(), s2.c_str()) >= 0);
}
inline bool operator<(const string& s1, const string& s2)
{
return (strcmp(s1.c_str(), s2.c_str()) < 0);
}
inline bool operator<=(const string& s1, const string& s2)
{
return (strcmp(s1.c_str(), s2.c_str()) <= 0);
}
inline bool operator==(const string& s1, const string& s2)
{
return (strcmp(s1.c_str(), s2.c_str()) == 0);
}
输入,输出的重载
//这里为什么没有用友元呢? 因为我并没有访问私有变量
//而是通过公有函数间接访问的私有变量
ostream& operator<<(ostream& out, const string& s)
{
for (auto& ch: s) //范围for是需要迭代器去支持的
{
cout << ch;
}
return out;
}
//这里需要清理掉原有的内容
istream& operator>>(istream& in, string& s)
{
s.clear();
//in >> c 这样用就是错误的,cin读不到空格和\n
char c = in.get(); //cin里面的接口,类似于getchar,一次读一个字符, 可以读到空格和\n
while (c != ' ' && c != '\n')
{
s.push_back(c);
c = in.get();
}
return in;
}
//getline 是按行读取,可以读到空格的
istream& getline(istream& in, string& s)
{
s.clear();
char c;
c = in.get();
while (c != '\n')
{
s.push_back(c);
c = in.get();
}
return in;
}