第三章 STL
一个简单的string
基本框架
class string
{
public:
//string();
//string(const char* s );
string(const char* str = ""); // 构造函数
string(const string& s); // 拷贝构造
char* c_str()const; // 返回字符串的首地址
~string(); // 析构函数
size_t size()const; // 获取字符串的个数
char& operator[](size_t pos); // 重载[]
string& operator=(const string& s); // 重载=
private:
char* _str ;
};
析构函数
string::string() :_str(new char[1])
{
_str[0] = '\0';
}
string::string(const char* str)
:_str(new char[ strlen(str) + 1 ])
{
strcpy(_str, str); // 会拷贝\0;
}
//
缺省参数不能写nullptr,
字符串需要有一个"\0",兼容语言
// 这一个抵上面的两个
string::string(const char* str ) // 再声明时写上默认参数,这里就不能写了,会报错
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
拷贝构造
string::string(const string& s) // 只能用引用传值
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
c_str
string::~string()
{
delete[] _str;
_str = nullptr;
}
size()方法
size_t string::size()const {
return strlen(_str);
}
[]重载
char& string::operator[](size_t pos) { // 返回引用实现可以修改的功能
return _str[pos];
}
赋值=
string& string::operator=(const string& s) {
if (this != &s){
char* tmp = new char[strlen(s._str) + 1];
delete[] _str;
_str = tmp;
strcpy(_str, s._str);
}
return *this;
}
深浅拷贝的问题
如果不实现,默认的拷贝,只会复制_str的地址,析构时会释放两次相同的空间,程序会崩溃
namespace hek_copy {
// 深浅拷贝
class string
{
public:
string(const char* str = "")
:_str(new char(strlen(str) + 1))
{
strcpy(_str, str);
}
// 深拷贝 - 传统写法
//string s2(s1)
/*string(const string& s)
:_str(new char(strlen(s._str) + 1))
{
strcpy(_str, s._str);
}*/
// 现代写法
// string s2(s1)
string(const string& s)
:_str(nullptr) // tmp会调用析构函数,_str不初始化会报错
{
string tmp(s._str);
swap(_str, tmp._str);
}
~string()
{
delete[] _str;
_str = nullptr;
}
// s1 = s3
/*string& operator=(const string& s)
{
if (this != &s)
{
char* tmp = new char[strlen(s._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
}
return *this;
}*/
// s1 = s3
string& operator=(string s) // s 拷贝构造来的
{
swap(_str, s._str);
return *this;
}
private:
char* _str;
};
}
完善string
基本框架
// 拥有的必要参数
class string
{
public:
static size_t npos;
private:
char* _str;
int _size; // 已经有多少个有效字符
int _capacity; // 可以存有效个字符,\0不算
};
size_t string::npos = -1; // 类外定义
}
基本的方法
以下都是放在public限定符下的
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
~string()
{
delete[] _str;
_str = nullptr;
_capacity = _size = 0;
}
// 拷贝构造和赋值
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
//(*this).swap(tmp);
//this->swap(tmp);
swap(tmp); // 一样效果
}
// s1.swap(s)
void swap(string& s)
{
::swap(_str, s._str); // ::调的是全局域的swap
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
string& operator=(string s)
{
this->swap(s);
return *this;
}
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
const char* c_str()const
{
return _str;
}
char& operator[](size_t& pos) {
assert(pos < _size);
return _str[pos];
}
const char& operator[](const size_t& pos) const{
assert(pos >= 0);
return _str[pos];
}
增容
//将容量扩展到n
void reserve(size_t n) // reserve -> 储备 reverse -> 反向
{
char* newstr = new char[n + 1];
strcpy(newstr, _str);
delete[] _str;
_str = newstr;
_capacity = n;
}
//
void resize(size_t n, char ch = '\0')
{
if (n < _size) {
_str[n] = '\0';
_size = n;
}
if (n > _size) {
if (n > _capacity) {
reserve(n);
}
/*while (_size < n)
{
_str[_size] = ch;
_size++;
}
_str[_size] = '/0';*/
for (size_t i = _size; i < n; ++i) {
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
增添数据
void push_back(char ch) // 单个字符
{
if (_size == _capacity) {
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_str[_size + 1] = '\0';
_size++;
}
void append(const char* str) // 字符串
{
int len = strlen(str);
if (_size + len > _capacity) {
reserve(_size + len);
}
strcpy(_str + _size, str); // _str+_size 的位置上开始拷。
_size = strlen(str) + _size;
}
string& insert(size_t pos, char ch)
{
assert(pos <= _size && pos >=0);
if (_size == _capacity) {
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
reserve(newcapacity);
}
int end = _size;
while (end >= (int)pos) // 需要强转,当插入到0时,end-- -> -1 , 跟无符号比会报错
{
::swap(_str[end], _str[end+1]);
end--;
}
_str[pos] = ch;
_size++;
return *this;
}
string& insert(size_t pos, const char* str)
{
assert(pos <= _size && pos >= 0);
size_t len = strlen(str);
if (_size + len > _capacity) {
reserve(_capacity + len);
}
int end = _size;
while (end >= (int)pos)
{
::swap(_str[end + len], _str[end]);
end--;
}
int i = 0;
for (int i = 0; i < len; i++)
{
_str[pos++] = str[i];
}
_size = len+_size;
return *this;
}
删除
string& erase(size_t pos, size_t len = npos) // 从pos删除len个
{
assert(pos < _size);
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t begin = pos + len;
while ( begin <= _size)
{
::swap(_str[pos++], _str[begin++]);
}
_size -= len;
return *this;
}
}
查找
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)
{
char* p = strstr(_str, str);
if (p == nullptr)
{
return npos;
}
return p - _str;
}
重载关系运算符 和 +=
// 比较
bool operator>(const string& s)
{
int ret = strcmp(_str, s._str);
return ret < 0;
}
bool operator<(const string& s)
{
int ret = strcmp(_str, s._str);
return ret > 0;
}
bool operator==(const string& s)
{
int ret = strcmp(_str, s._str);
return ret == 0;
}
bool operator>=(const string& s)
{
return *this > s || *this == s; // 复用
}
bool operator<=(const string& s)
{
return *this < s || *this == s;
}
bool operator!=(const string& s)
{
return !(*this == s);
}
string& operator+=(const char ch) {
this->push_back(ch);
return *this;
}
string& operator+=(const char* str) {
this->append(str);
return *this;
}
一些特殊的重载
迭代器
typedef char* iterator;
iterator begin() { // 方法名不建议变化,如变了,范围for循环将不能使用
return _str;
}
iterator end() {
return _str + _size;
}
重载流运算符
在类外定义。实现 cout<<str , cin>>str 的形式
istream& operator>>(istream& in, string& s)
{
while (1)
{
char ch;
//in >> ch; // 会跳过空格和回车
ch = in.get();
if (ch == ' ' || ch == '\n')
{
break;
}
else
{
s += ch;
}
}
return in;
}
ostream& operator<<(ostream& out,const string& s )
{
for (int i = 0; i < s.size(); i++)
{
out << s[i] ;
}
return out;
}