主要写增删查改和几种常见的构造方式:
文章目录
定义先确定string类中成员变量:
private:
char* _str;
size_t _size;//实际空间
size_t _capacity; //有效空间,真实容量没算\0;
static const size_t npos;//位置
};
1.1第一种构造方式 string(const char *s) :
//s1("HAOHAO")
string(const char* str)
:
_size(strlen(str))//开空间初始化
, _capacity(_size)
{
_str=new char[_capacity + 1];
strcpy(_str, str);
}
1.2.第二种构造方式拷贝构造://3.string(const string & str,string size_type n = npos):
这里需要用深拷贝,浅拷贝会报错,浅拷贝并没有直接开辟空间而是指针指向了被拷贝的空间,看起来没什么问题,但是程序结束时会报错,因为结束时会调用析构函数,按照下图所示s2会先调用析构函数,之后s1也会调用析构函数,此时s1的空间已经被s2释放了所以程序报错;
string s1("HAOHAO");
string s2=(s1);
深拷贝又有好几种写法,分为古代和现代两种写法
//深拷贝古代写法:啥都亲力亲为
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);//这里s._str的类型是char*所以调用的是我们自己写的第一种构造方式
//这个swap是我们自己写的函数,后面重载操作符 =的时候会说为啥要自己写
this->swap(tmp);
}
1.3.重载操作符 =:
这也分了古代和现代两种写法,现代中用了我们自定义的swap函数,如果用swap(_str,s.
str)这么写只是把指针_str和s
.str这两个指针互换了,this的容量并没有交换无法全部拷贝到
//s1=s3;古代写法
string& operator=(const string& s) {
//释放掉原空间,避免浪费,可能s1很大s3很小
if (this!= &s) {//s3=s3这种情况
char* tmp = new char(s._size);//先开辟空间再释放
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;//传的地址
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
//现代写法
string & operator=(const string &s) {
if (this != &s) {
string tmp(s);
//普通的swap(_str,tmp_str)只是交换了_str指针的方向但是string类里size和capacity还跟以前一样,会出问题
this->swap(tmp);
cout << "交换后" << endl;
cout << this->_str << endl;
\
}
return *this;
}
void swap(string& s) {
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
1.4.重载运算符[ ]、s._str()、size()函数:
char& operator[](size_t pos) {
assert(pos < _size);//防止越界直接断死
return _str[pos];
}
const char* c_str() {
return _str;
}
size_t size()const {
return _size;
}
1.5.查找单个字符和字符串:
//查找单个字符
size_t find(const char ch) {
for (size_t i = 0; i < _size; i++) {
if (ch == _str[i]) {
return i;
}
}
return npos;//没有找到返回npos
}
//查找字符串
size_t find(const char *s,size_t pos=0) {
for (size_t i = 0; i < _size; i++) {
//:strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
const char* ptr = strstr(_str + pos, s);
if (ptr == nullptr) {
return npos;
}
else {
return ptr - _str;
}
}
return npos;//没有找到返回npos
}
1.6.reserve()和resize()函数:
扩容reserve和resize: reserve的逻辑是先把内容拷贝下来,存在tmp中再把原来的空间给删掉;resize的容量可能会被变小,变小的话直接在n的位置加上’\0’,然后改变_size,如果变大又分了两种情况(1)n比_size大比_capacity小,(2)n直接比capacity大。
第一种情况直接在比 下标size 大的填上‘\0’占位;
第二种情况先扩容再占位;
void reserve(size_t n) {//改变capacity n比原来的那么就变成n 比原来小就不变
if (n > _capacity) {
char* tmp = new char[n + 1];//\0
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char ch= '\0') {
if (n <= _size) {//resize跟reserve不一样它如果resize一个比它之前size小的数会缩容量的,最后一个位置填‘\0’
_str[n] = '\0';
_size = n;
}
else {
if (n > _capacity) {
reserve(n);
}
memset(_str + _size, ch, n - _size);//扩容后把空间填充从size开始
_str[n] = '\0';
_size = n;
}
}
1.7.push_back()函数:
void push_back(char ch) {
//判断是否为空和是否已经满了,如果本身没有空间扩容四个字节,如果本身有空间但是满了直接扩容两倍
if (_size == _capacity) {
reserve(_capacity==0?4: _capacity *2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
1.8.inset()函数:
指定位置插入字符或字符串:先判断容量是否够不够就扩,然后把从最后一个字符往后移,直到pos位置为空,end一定要在最后一个字符的后面一个位置也就是‘\0’的位置,如果end=_size,判断条件while (end>=(pos) ,如果此时在第0个位置插入end最后会变成-1程序歇火;
插入字符串也是一样的也是从最后面开始移动但是end要在size+len(插入字符串长度)的位置,直到找到pos
//插入字符
string& insert(size_t pos, char ch) {
assert(pos <= _size);
//判断容量是否满了
if (_size == _capacity) {
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//这么写 在第0个位置插入时会报错,end变为-1直接歇火size_t类型不能为负数,转为int的时候while (end>=pos)这个判断还会出问题要
// while (end>=(int)pos)
//size_t end = _size;
往后移直到第pos个位置为空
//while (end>=pos) {
// _str[end + 1] = _str[end];
// --end;
//}
//直接 HAO\0,把end放在斜杠0后面
size_t end = _size+1;
while (end > pos) {
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
++ _size;
return *this;
}
//插入字符串
string& insert(size_t pos,const char *ch) {
assert(pos <= _size);
//先看容量够不够
size_t len = strlen(ch);
if (_size + len > _capacity) {
reserve(_size+len);
}
size_t end = _size + len;
while (end > =pos+len) {
_str[end] = _str[end - len];
end--;
}
strncpy(_str, ch, len);
_size+=len;
return *this;
}
1.9 append()函数:
在末尾插入字符串扩容时要结合实际情况扩容,还有一种简便写法直接调用上面写的insert函数
void append(const char* ch) {
//扩容不能单纯的扩两倍可能不够万一要结合实际
size_t len = strlen(ch);
if (_size + len > _capacity) {
this->reserve(_size+len);
}
strcpy(_str + _size, ch);//从上一个单词的末尾插
}
void append(const char* ch) {
insert(_size, ch);
}