目录
string类
namespace xb
{
class string
{
public:
//各函数接口
//.....
private:
size_t _size;
size_t _capacity;
char* _s;//成员函数的排列顺序,决定谁先初始化(与初始化列表顺序无关)
// const static 语法特殊处理
// 直接可以当成定义初始化
const static size_t npos = -1;
};
注:
_size存储有效字符个数
_capacity是字符的内存空间大小(包含 '\0' )
一般static修饰的成员变量,要在类外定义,但const static可以直接当成定义初始化
构造函数
string(const char* str = "")//构造
{
_size = strlen(str);
_capacity = _size;
_s = new char[_capacity + 1];
strcpy(_s, str);
}
析构函数
~string()//析构
{
delete[] _s;
_s = nullptr;
_size = _capacity = 0;
}
拷贝构造函数
我们在实现之前需要了解深浅拷贝的定义
浅拷贝:拷贝出来的目标对象指向和源对象相同的空间,程序结束时该空间会被析构两次,且修改其中一个对象的数据,另一个对象也会被改变
深拷贝:拷贝出来的目标对象与源对象指向不同的空间,两对象互不影响
因此要想实现拷贝构造函数,我们需要进行深拷贝
传统写法:
string(const string& str)
:_s(new char[_capacity+1])
,_size(str._size)
,_capacity(str._capacity)
{
strcpy(_s, str._s);
}
现代写法:
void swap(string& tmp)//在类里面实现一个swap
{
::swap(_s, tmp._s);
::swap(_size, tmp._size);
::swap(_capacity, tmp._capacity);
}
//s2(s1)
string(const string& str)//拷贝
:_s(nullptr)
, _size(0)
, _capacity(0)
{
string tmp(str._s);
swap(tmp);
}
赋值运算符重载函数
与拷贝构造函数一样,赋值运算符重载函数也进行深拷贝
传统写法:
//s2=s1
string& operator=(const string& str)
{
if (this != &str)//判断传入的string是否是他本身
{
char* tmp = new char[str._capacity + 1];
strcpy(tmp, str._s);
delete[] _s;
_s = tmp;
_size = str._size;
_capacity = str._capacity;
}
return *this;
}
现代写法:
tmp作打工人,将拷贝好的数据与this->_s交换
//s2=s1
string& operator=(const string& str)
{
if (this != &str)
{
string tmp(str._s);
swap(tmp);
}
return *this;
}
str代替tmp做打工人,直接将拷贝的数据与this->_s交换
//s2=s1
//str代替tmp做打工人
string& operator=(string str)
{
swap(str);
return *this;
}
迭代器相关函数
string类中的迭代器实际上就是字符指针,只是给字符指针起了一个别名叫iterator而已。
typedef char* iterator;
typedef const char* const_iterator;
注:不是所有迭代器都是指针
begin和end
iterator begin():是返回字符串中第一个字符的地址
iterator begin()
{
return _s;
}
const_iterator begin()const
{
return _s;
}
iterator end():返回字符串中最后一个字符的下一个位置的地址
iterator end()
{
return _s + _size;
}
const_iterator end()const
{
return _s + _size;
}
容量和大小相关函数
size和capacity
size_t size()const:获取字符串当前的有效长度(不包含'\0')
size_t size()const
{
return _size;
}
size_t capacity()const:用于获取字符串当前的容量
size_t capacity()const
{
return _capacity; //返回字符串当前的容量
}
reserve和resize(改变容量)
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _s);
delete[] _s;
_s = tmp;
_capacity = n;
}
}
注:
a.当n大于对象当前的capacity时,将capacity扩大到n或大于n。
b.当n小于对象当前的capacity时,什么也不做。
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
// 插入数据
reserve(n);
for (size_t i = _size; i < n; i++)
{
_s[i] = ch;
}
_size = n;
_s[n] = '\0';
}
else
{
//删除数据
_s[n] = '\0';
_size = n;
}
}
注:a.当n大于当前的size时,将size扩大到n,扩大的字符为ch,若ch未给出,则默认为’\0’。
b.当n小于当前的size时,将size缩小到n。
修改字符串相关函数
push_back
push_back(char ch):在当前字符串的后面尾插上一个字符ch
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_s[_size] = ch;
_size++;
_s[_size] = '\0';
}
append
append(const char* str):在当前字符串的后面尾插一个字符串str
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_s + _size, str);
//strcat(_s,str);//需要找'\0',效率低
_size += len;
}
append(const string& str):在当前字符串的后面尾插一个string类型的变量str
void append(const string& str)
{
append(str._s);
}
append(size_t n, char ch): 在当前字符串的后面尾插n个字符ch
void append(size_t n, char ch)
{
reserve(_size + n);
for (size_t i = 0; i < n; ++i)
push_back(ch);
}
operator+=
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);//直接复用append()
return *this;
}
insert
string& insert(size_t pos, char ch):在字符串的任意位置插入字符
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)//注意:判断条件
{
_s[end] = _s[end - 1];
end--;
}
_s[pos] = ch;
_size++;
return *this;
}
string& insert(size_t pos, const char* str): 在字符串的任意位置插入字符串
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
// 挪动数据 ----从'\0'开始挪动
size_t end = _size + len;
while (end > pos + len)//注意:判断条件
{
_s[end] = _s[end - len];
end--;
}
strncpy(_s + pos, str, len);
_size += len;
return *this;
}
erase
erase(size_t pos, size_t len = npos):删除字符串任意位置开始的n个字符
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_s[pos] = '\0';
_size = pos;
}
else
{
strcpy(_s + pos, _s + pos + len);
_size -= len;
}
}
clear
clear():将对象中存储的字符串置空
void clear()
{
_s[0] = '\0';
_size = 0;
}
swap
void swap(string& tmp)
{
::swap(_s, tmp._s);
::swap(_size, tmp._size);
::swap(_capacity, tmp._capacity);
}
c_str
const char* c_str()const:获取对象C类型的字符串
const char* c_str()const
{
return _s;
}
访问字符串相关函数
operator[ ]
char& operator[](size_t pos):让string对象可以像数组一样,通过下标来访问每个字符
char& operator[](size_t pos)
{
assert(pos < _size);
return _s[pos];
}
const char& operator[](size_t pos)const
{
assert(pos < _size);
return _s[pos];
}
find
size_t find(char ch, size_t pos = 0) const:从pos位置开始向后寻找与之匹配的第一个字符的下标
size_t find(char ch, size_t pos = 0) const
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_s[i] == ch)
return i;
}
return npos;
}
size_t find(const char* sub, size_t pos = 0) const:从pos位置开始向后寻找与之匹配的第一个子串的地址
//hello string !!! --- "ll"
size_t find(const char* sub, size_t pos = 0) const
{
assert(pos < _size);
assert(sub);
const char* ptr = strstr(_s + pos, sub);//strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _s;
}
}
关系运算符重载函数
bool operator>(const string& s) const
{
return strcmp(_s, s._s) > 0;
}
bool operator==(const string& s) const
{
return strcmp(_s, s._s) == 0;
}
复用已有的重载运算符'>'和'=='
bool operator>=(const string& s) const
{
return (*this > s) || (*this == s);
}
bool operator<=(const string& s) const
{
return !(*this > s);
}
bool operator<(const string& s) const
{
return !(*this >= s);
}
bool operator!=(const string& s) const
{
return !(*this == s);
}
>>和<<运算符的重载
ostream& operator<<(ostream& out, const string& str)
{
for (size_t i = 0; i < str.size(); i++)
{
out << str[i];
}
return out;
}
istream& operator>>(istream& in, string& str)
{
char ch;
//in >> ch;//>>会跳过' '和'\n'读取,无法停止读取
ch = in.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = in.get();
}
return in;
}
上述>>重载函数的写法,如果输入字符串很长,不断+=,频繁扩容,效率很低
优化:
创建一个buff数组来存储N个字符,存满后再+=到str上,减少了reserve的调用
istream& operator>>(istream& in, string& str)
{
char ch = 0;
ch = in.get();
const size_t N = 32;
char buff[N];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 31)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = in.get();
}
buff[i] = '\0';
str += buff;
return in;
}
get()
get():可用于读取含有空格的字符串,当读取到’\n’的时候才停止读取字符。
string类的基本实现
namespace xb
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _s;
}
iterator end()
{
return _s + _size;
}
const_iterator begin()const
{
return _s;
}
const_iterator end()const
{
return _s + _size;
}
//string()//string以'\0'结尾
// :_s(new char[1])
// ,_size(0)
// ,_capacity(0)
//{
// _s[0] = '\0';
//}
string(const char* ch = "\0")
//string(const char* ch = "")
// :_s(new char[_capacity + 1])//给结尾的'\0'开空间
// , _size(strlen(ch))
// , _capacity(_size)
//{
// strcpy(_s, ch);
//}
string(const char* str = "")//构造
{
_size = strlen(str);
_capacity = _size;
_s = new char[_capacity + 1];
strcpy(_s, str);
}
~string()//析构
{
delete[] _s;
_s = nullptr;
_size = _capacity = 0;
}
//现代写法
void swap(string& tmp)
{
::swap(_s, tmp._s);
::swap(_size, tmp._size);
::swap(_capacity, tmp._capacity);
}
//s2(s1)
string(const string& str)//拷贝
:_s(nullptr)
, _size(0)
,_capacity(0)
{
string tmp(str._s);
swap(tmp);
}
//s2=s1
/*string& operator=(const string& str)
{
if (this != &str)
{
string tmp(str._s);
swap(tmp);
}
return *this;
}*/
//s2=s1
//str代替tmp做打工人
string& operator=(string str)
{
swap(str);
return *this;
}
传统写法
s2(s1)
//string(const string& str)
// :_s(new char[_capacity+1])
// ,_size(str._size)
// ,_capacity(str._capacity)
//{
// strcpy(_s, str._s);
//}
s2=s1
//string& operator=(const string& str)
//{
// if (this != &str)
// {
// char* tmp = new char[str._capacity + 1];
// strcpy(tmp, str._s);
// delete[] _s;
// _s = tmp;
// _size = str._size;
// _capacity = str._capacity;
// }
// return *this;
//}
size_t size()const
{
return _size;
}
const char* c_str()const
{
return _s;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _s[pos];
}
const char& operator[](size_t pos)const
{
assert(pos < _size);
return _s[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _s);
delete[] _s;
_s = tmp;
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
// 插入数据
reserve(n);
for (size_t i = _size; i < n; i++)
{
_s[i] = ch;
}
_size = n;
_s[n] = '\0';
}
else
{
//删除数据
_s[n] = '\0';
_size = n;
}
}
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_s[_size] = ch;
_size++;
_s[_size] = '\0';//符合string的基本要求
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_s + _size, str);
//strcat(_s,str);//需要找'\0',效率低
_size += len;
}
void append(const string& str)
{
append(str._s);
}
void append(size_t n, char ch)
{
reserve(_size + n);
for (size_t i = 0; i < n; ++i)
push_back(ch);
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
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)//注意:判断条件
{
_s[end] = _s[end-1];
end--;
}
_s[pos] = ch;
_size++;
return *this;
}
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
// 挪动数据 ----从'\0'开始挪动
size_t end = _size + len;
while(end > pos+len )//注意:判断条件
{
_s[end] = _s[end - len];
end--;
}
strncpy(_s+pos, str, len);
_size += len;
return *this;
}
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_s[pos] = '\0';
_size = pos;
}
else
{
strcpy(_s + pos, _s + pos + len);
_size -= len;
}
}
size_t find(char ch, size_t pos = 0) const
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_s[i] == ch)
return i;
}
return npos;
}
//hello string !!! --- "ll"
size_t find(const char* sub, size_t pos = 0) const
{
assert(pos < _size);
assert(sub);
const char* ptr =strstr(_s + pos, sub);//strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _s;
}
}
string substr(size_t pos, size_t len = npos) const
{
assert(pos < _size);
size_t lenth = len;
if (len == npos || pos + len > _size)
{
lenth = _size - pos;
}
string str;
for (size_t i = 0; i < lenth; i++)
{
str += _s[pos + i];
}
return str;
}
void clear()
{
_s[0] = '\0';
_size = 0;
}
bool operator>(const string& s) const
{
return strcmp(_s, s._s) > 0;
}
bool operator==(const string& s) const
{
return strcmp(_s, s._s) == 0;
}
bool operator>=(const string& s) const
{
return (*this > s) || (*this==s);
}
bool operator<=(const string& s) const
{
return !(*this > s);
}
bool operator<(const string& s) const
{
return !(*this >= s);
}
bool operator!=(const string& s) const
{
return !(*this == s);
}
private:
size_t _size;
size_t _capacity;
char* _s;//成员函数的排列顺序,决定谁先初始化(与初始化列表顺序无关)
// const static 语法特殊处理
// 直接可以当成定义初始化
const static size_t npos = -1;
};
ostream& operator<<(ostream& out, const string& str)
{
for (size_t i = 0; i < str.size(); i++)
{
out << str[i];
}
return out;
}
//istream& operator>>(istream& in, string& str)
//{
// char ch;
// //in >> ch;//>>会跳过' '和'\n'读取,无法停止读取
// ch = in.get();
// while (ch != ' ' && ch != '\n')
// {
// str += ch;
// ch = in.get();
// }
// return in;
//}
// 输入字符串很长,不断+=,频繁扩容,效率很低
//优化:
//
istream& operator>>(istream& in, string& str)
{
char ch = 0;
ch = in.get();
const size_t N = 32;
char buff[N];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 31)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = in.get();
}
buff[i] = '\0';
str += buff;
return in;
}
}