一、默认成员函数
1.成员变量
cpp
private:
char* _str;
//string对象中字符串的长度
size_t _size;
//string对象中可容纳字符串长度
size_t _capacity;
public:
const static size_t npos;
//static类型的成员变量只能在类外初始化
const size_t string::npos = -1;
注意:_capacity是可容纳的字符长度,不包含'\0',在为string类开空间时要多一个长度来放'\0',即默认构造时要开辟_capacity+1大小。
2.默认构造
cpp
//构造函数
string(const char* str = "")
{
size_t len = strlen(str);
_capacity = len;
_size = len;
//开辟_capacity+1个空间是因为还有一个'\0'
_str = new char[_capacity + 1];
//把str的内容拷贝给_str,要注意最后的'\0'
memcpy(_str, str, _capacity + 1);
}
注意:这里的拷贝str内容要用memcpy,不能用strcpy,因为strcpy遇到'\0'就停止了,拷贝不到'\0'。
3.拷贝构造
cpp
string(const string& s)
{
_capacity = s._capacity;
_size = s._size;
//开辟_capacity+1个空间是因为还有一个'\0'
_str = new char[_capacity + 1];
//把str的内容拷贝给_str,要注意拷贝到'\0'
memcpy(_str, s._str, _capacity + 1);
}
注意:拷贝构造是深拷贝。
4.operator=
cpp
//传进来一个临时拷贝来的局部变量s,出作用域销毁
void swap(string& s)
{
std::swap(s._str, _str);
std::swap(s._size, _size);
std::swap(s._capacity, _capacity);
}
string& operator=(string s)
{
//v3,自己函数重载一个swap函数
swap(s);
return *this;
}
注意:这里的operator=传的参数是 string s,在传参时会调用拷贝构造(深拷贝) 生成一个局部变量s,等函数结束时会自动调用string的析构函数释放掉,再通过自己写的swap来交换这两个string对象的成员,swap函数的实现调用的是c++库中的swap。这里巧妙地运用了局部自定义变量出作用域会自动调用析构函数的特性,使我们不用自己去开空间。
5.析构函数
cpp
~string()
{
delete[] _str;
_capacity = 0;
_size = 0;
}
注意:开辟空间时用的new [] ,释放空间就要用 delete []。
二、迭代器
1.begin和end
cpp
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
const_iterator begin()const
{
return _str;
}
//end的位置是字符串末尾的下一个位置,所以要加_size
iterator end()
{
return _str + _size;
}
const_iterator end()const
{
return _str + _size;
}
注意:这里要生成const类型的迭代器和非const类型的迭代器以应对不同类型的使用情况。还要注意end的位置是字符串末尾的下一个位置。
rbegin和rend的实现类似。可以自己试一试。
2.c_str
cpp
const char* c_str()const
{
return _str;
}
注意:c_str是为了兼容c语言的接口,返回的是一个char*的字符串。
可以这样用:string s("abcdef");printf("%s\n",s.c_str());
就会打印出abcdef。
3._capacity相关的接口
3.1 size,capacity,empty
cpp
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
注意:_size和_capacity都是private的私有成员变量,用接口来获得相应数据。
//this指针本质是Type* const pointer ,不改变*this时,可以给*this加上const--->const Type* const pointer
cpp
bool empty()const
{
return _size == 0;
}
注意:_size是表示string对象的有效字符个数,当它为0时表示string对象为空。
3.2 reserve
cpp
//传的参数是你要开的有效空间,在内部会多开一个空间放'\0'
void reserve(size_t n)
{
//先判断容量
//不缩容
if (n >= _capacity)
{
char* tmp = new char[n + 1];
//要把'\0'拷贝到新的_str里
memcpy(tmp, _str, _size + 1);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
reserve一般不缩容,只扩容,扩容时只需要开一个n+1长度的空间,保存_str的数据,释放_str的空间,刷新_capacity的数据即可。
3.3 resize
cpp
//有两种情况,扩大/缩小
void resize(size_t size, char c = '\0')
{
//缩小
if (size < _size)
{
_size = size;
_str[_size] = c;
}
else
{
reserve(size);
memset(_str + _size, c, (size + 1 - _size));
_size = size;
_str[_size] = '\0';
}
}
注意:缩小是简单,只要把_size刷新成你传的参数size大小,在把size位置的值改成'\0',就完成了resize的缩小。扩大时,要考虑扩容,即把它扩容到size大小,可以用reserve来扩容,然后再把新扩容的位置的值,刷新为'\0'即可,char c='\0'给的是缺省参数,你可以把扩容的部分初始化成其他值。
4.modify相关接口
4.1 push_back
cpp
void push_back(char ch)
{
//先判断容量
if (_size >= _capacity)
{
//string用空串初始化(默认构造)时,_capacity和_size都为0,这里的_capacity不能直接*2
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
注意:尾插时要先判断容量再尾插
4.2 append
cpp
void append(const char* str)
{
size_t len = strlen(str);
//先判断容量
if (_size + len > _capacity)
{
reserve(_size + len);
}
//这里要把str的'\0'也拷贝进来
memcpy(_str + _size, str, len + 1);
//_capaciry在reserve中处理,这里只要改_size即可
_size += len;
}
注意:尾插一段字符串,也要先扩容再尾插。
5.operator运算符
5.1 operator+= c/str
cpp
string& operator+=(char c)
{
push_back(c);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
这里可以复用push_back和append.
5.2 operator[] 非常好用
cpp
char& operator[](size_t index)
{
return _str[index];
}
const char& operator[](size_t index) const
{
return _str[index];
}
5.3 operator>,>=,<,<=,==,!=
cpp
bool operator<(const string& s)
{
//比较较短的串,
int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
// "hello" "hello" false ,ret==0------_size<s._size为假 返回false
// "helloxx" "hello" false ,ret==0------_size<s._size为假 返回false
// "hello" "helloxx" true ,ret==0------_size<s._size为真 返回true
//普通情况 "elloxx" "helloxx",ret!=0------ret<0为真 返回true (e<h)
//普通情况 "helloxx" "elloxx",ret!=0------ret<0为假 返回false (e<h)
return ret == 0 ? _size < s._size : ret < 0;
}
bool operator<=(const string& s)
{
return *this == s || *this < s;
}
bool operator>(const string& s)
{
return !(*this <= s);
}
bool operator>=(const string& s)
{
return !(*this < s);
}
bool operator==(const string& s)
{
return _size == s._size && memcmp(_str, s._str, _size) == 0;
}
bool operator!=(const string& s)
{
return !(*this == s);
}
注意:这里只要完成一个函数,其他的都可以复用。
5.4 opoerator>>和operator<<
cpp
//先在类域中声明 友元函数
public:
friend std::ostream& operator<<(std::ostream& _cout, const mystring::string& s);
friend std::istream& operator>>(std::istream& _cin, mystring::string& s);
operator>>
cpp
//流插入本质是用新的值覆盖旧的数据,先用clear把它的数据清空
void clear()
{
_size = 0;
_str[0] = '\0';
}
std::istream& operator>>(std::istream& _cin, mystring::string& s)
{
s.clear();
char ch = _cin.get();
//处理缓冲区的换行和空格
while (ch == ' ' || ch == '\n')
{
ch = _cin.get();
}
//这里为了防止它频繁扩容,为他加了一个数组,一段一段的拷贝到string 对象里,减少扩容次数
char buf[128];
int i = 0;
while (ch != ' ' && ch != '\n')
{
buf[i++] = ch;
if (i == 127)
{
//防止拷贝时,没有结束符
buf[i] = '\0';
s += buf;
i = 0;
}
ch = _cin.get();
}
return _cin;
}
operator<<
cpp
std::ostream& operator<<(std::ostream& _cout, const mystring::string& s)
{
for (auto ch : s)
{
_cout << ch;
}
return _cout;
}
这里用的范围for本质是调用寄存器来实现的。
6.find相关接口
6.1 find
cpp
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{
assert(pos <= _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == c)
{
return i;
}
}
return npos;
}
// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{
assert(pos <= _size);
const char* tmp = strstr(_str + pos, s);
if (tmp == nullptr)
{
return npos;
}
else
{
return tmp - _str;
}
}
注意:在查找之前要先判断pos是否合法。
6.2 substr
cpp
string substr(size_t pos = 0, size_t len = npos) const
{
assert(pos <= _size);
size_t n = len;
if (len == npos || len + pos > _size)
{
n = _size - pos;//这个n的长度是包含'\0'的
}
string tmp;
//开空间
tmp.reserve(n);
for (size_t i = pos; i < pos + n; i++)
{
tmp += _str[i];
}
return tmp;
}
返回一个字串。
6.3 insert
cpp
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c)
{
assert(pos <= _size);
if (_size + 1 >= _capacity)
{
reserve(_capacity + 1);
}
memmove(_str + pos + 1, _str + pos, _size - pos + 1);
_str[pos] = c;
return *this;
}
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
int len = strlen(str);
if (_size + len >= _capacity)
{
reserve(_size + len);
}
memmove(_str + pos + len, _str + pos, _size + 1 - pos);
_size = _size + len;
memcpy(_str + pos, str, len);
return *this;
}
注意:这里只能用memmove防止内存重叠。
6.4 erase
cpp
// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len = npos)
{
assert(pos <= _size);
if (len != npos && len + pos >= _size)
{
len = _size - pos;
}
memmove(_str + pos, _str + pos + len, _size + 1 - pos - len);
_size = _size - len;
return *this;
}
注意:这里只能用memmove防止内存重叠。
总结
想要学好库中的string最好自己实现一个出来,能够加深对string的理解。
蟹蟹观看!点赞!关注!收藏!评论!一键三连!