C++_string类

目录

一、string的模拟实现

1、初始化字符串

2、拷贝构造

3、赋值重载 

4、迭代器

5、比较字符串

6、尾插字符、字符串

7、resize

8、中间插入数据、删除数据

8.1 插入数据

8.2 删除数据

9、查找数据

10、打印对象(流插入、流提取)

结语:


前言:

        C++中的string类是专门用于处理字符串的,比如对字符串的增删查改、以及对字串进行各种操作,当然,上面说到的这些在c语言中一样可以使用字符数组实现,那为什么还要费尽心思的去专门实现一个类来解决字符串相关的问题呢,原因就是C++中的string类对边界访问更严格,并且用string类操作字符串相对于c语言中操作字符数组更加简便。

一、string的模拟实现

        string类是STL(标准模板库)中的八大容器之一,STL又是C++中标准库的一部分,简单来说就是库里面已经写好了一个string类,程序员只需要调用该string类就能操作字符串(使用库里的string类要包含头文件<string>),调用该类很简单,但是只有通过了解string类的底层实现是如何实现,并且自己模拟实现出一个string,才能更进一步的了解string类。

1、初始化字符串

        首先写一个类,可以实现字符串的创建和打印,初始化字符串代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}size_t size()const//私有域不可直接被外部访问,因此需要用函数返回_size{return _size;}char& operator[](size_t i)//为了让外部能用[]访问字符串的内容{assert(i < _size);return _str[i];}const char* c_str()//返回首元素地址(类似数组名的作用){return _str;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小};
}int main()
{ZH::string st1("hello world");ZH::string st2;for (size_t i = 0; i < st1.size(); i++){cout << st1[i] << " ";}cout << endl;for (size_t i = 0; i < st2.size(); i++){cout << st2[i] << " ";}cout << endl;cout << st1.c_str() << endl;//用c语言的打印字符串方式,传首地址打印cout << st2.c_str() << endl;return 0;
}

        运行结果:

        string类的成员变量具体含义作用如下图所示:

2、拷贝构造

        我们知道拷贝构造如果不自己实现,那么系统会自动生成一个拷贝构造并且调用,但是系统自动生成的拷贝构造只能完成浅拷贝(即值拷贝)的场景,比如string类的拷贝就不能用浅拷贝完成,具体原因如下:

        因此系统自己生成的浅拷贝不能完成这类场景的拷贝,需要我们手动写一个拷贝构造函数完成深拷贝。

        演示拷贝构造代码如下(将拷贝构造代码放到上文的代码中也同样可以实现,这里省去了与拷贝构造代码无关的代码):

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}const char* c_str()//返回首元素地址(类似数组名的作用){return _str;}string(const string& s)//深拷贝-拷贝构造:_size(s._size), _capacity(s._capacity){_str = new char[_capacity + 1];//开辟一块独立的空间strcpy(_str, s._str);}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小};
}int main()
{ZH::string st1("hello world");ZH::string st2(st1);//将st1的内容拷贝给st2cout << st1.c_str() << endl;//用c语言的打印字符串方式,传首地址打印cout << st2.c_str() << endl;return 0;
}

         运行结果:

3、赋值重载 

        赋值的时候要考虑以下几个问题:

        由于以上几个问题涉及的点太多,而且过程过于复杂或造成不必要的消耗,因此库里面的string类在面对赋值时是重新开辟一块空间然后把s1的数据拷贝到该空间中,并且让s2指向该空间即可。

        模拟实现的赋值重载代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}const char* c_str()//返回首元素地址(类似数组名的作用){return _str;}string& operator=(const string& s)//赋值重载{if (&s != this){char* temp = new char[s._capacity+1];//重新开辟一块空间strcpy(temp, s._str);//把数据拷贝到该空间中delete[] _str;//释放拷贝对象的原先空间内容_str = temp;//让拷贝对象指向该空间_size = s._size;_capacity = s._capacity;}return *this;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小};
}int main()
{ZH::string st1("hello world");ZH::string st2("zdzdzzdzd");st2 = st1;cout << st1.c_str() << endl;//用c语言的打印字符串方式,传首地址打印cout << st2.c_str() << endl;return 0;
}

        运行结果:

4、迭代器

        在string类中,他的迭代器可以理解成是由一个指针实现的,不仅可以遍历打印字符串,还能够修改字符串中的内容,因此string类中迭代器的实现很简单,只需要一个指向首元素的指针,和一个指向尾部的指针(注意:尾部表示最后一个元素的下一个位置,因为迭代器区间是一个左闭右开的区间)即可。


        迭代器模拟实现代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public://用typedef来模拟迭代器的两种类型typedef char* iterator;typedef const char* const_iterator;string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}iterator begin()//返回首地址{return _str;}iterator end()//返回末尾地址{return _str + _size;}const_iterator end()const//const版本迭代器{return _str + _size;}const_iterator begin()const//const版本迭代器{return _str;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小};
}int main()
{ZH::string st1("hello world");ZH::string st2("hello world");ZH::string::iterator it = st1.begin();while (it!=st1.end()){(*it)++;cout << *it << " ";it++;}cout << endl;ZH::string::const_iterator cit = st2.begin();while (cit != st2.end()){//(*cit)++;//被const修饰的迭代器是不能通过该迭代器修改对象里面的内容cout << *cit << " ";cit++;}cout << endl;return 0;
}

        运行结果:

        总结:迭代器的模拟实现实际上就是begin()和end()两个成员函数的返回指针构成的。 

5、比较字符串

        在字符数组中,只能通过strcmp函数对比两个字符串的大小(按照元素的ASCII码值进行比较),每次对比都需要调用strcmp,写起来也麻烦。然而在string类中可以使用比较运算符进行字符串的对比,比如:‘<','>','<=','>=','==','!=',这些平时用于内置类型的比较运算符如今也可以用在string类(自定义类型)中,原因就是类里面实现了运算符重载。

        当对象支持了运算符重载,则对象与对象之间的对比写起来就更加简单了,模拟实现string运算符重载代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public://用typedef来模拟迭代器的两种类型typedef char* iterator;typedef const char* const_iterator;string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}bool operator>(const string& s)const{return strcmp(this->_str, s._str) > 0;}bool operator<(const string& s)const{return strcmp(this->_str, s._str) < 0;}bool operator==(const string& s)const{return strcmp(this->_str, s._str) == 0;}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);}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小};
}int main()
{ZH::string st1("jello world");ZH::string st2("hello world");cout << (st1 > st2) << endl;//1cout << (st1 < st2) << endl;//0cout << (st1 == st2) << endl;//0cout << (st1 != st2) << endl;//1cout << (st1 <= st2) << endl;//0cout << (st1 >= st2) << endl;//1return 0;
}

        运行结果:

        可以以上代码可以看到,实际上还是复用了strcmp这个函数,只不过对其进行了又一层的封装,这么做的目的就是为了在主函数中可以直接用‘<','>','<=','>=','==','!='进行对象之间的直接运算,总结就是:底层变得复杂,使用变得简单。

6、尾插字符、字符串

        尾插数据时,需要考虑容量是否足够,实现尾插字符串时可以使用函数strcpy进行复用。尾插字符时就如同在数组插入新元素一样。

        扩容函数为reserve,他的作用是单纯的开空间,增大容量,不会对空间内的数据进行修改,即只会改变_capacity的值,不会改变_size的值。(注意扩容的时候也要为’\0'单独开一块空间,但是不会把该空间记录到_capacity中

        尾插代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}void reserve(size_t n)//扩容函数{if (n > _capacity)//只有n大于_capacity才需要扩容{char* temp = new char[n + 1];if (_str != nullptr){strcpy(temp, _str);delete[] _str;}_str = temp;_capacity = n;}}void push_back(char ch)//尾插字符{if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size++] = ch;_str[_size] = '\0';}void append(const char* str)//尾插字符串{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}const char* c_str(){return _str;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小};
}int main()
{ZH::string st1;st1.push_back('h');st1.push_back('e');st1.push_back('l');st1.push_back('l');st1.push_back('o');st1.append(" world");cout << st1.c_str() << endl;return 0;
}

        运行结果:


        如果每次尾插数据时还需要调用成员函数,则就体现不出string类的优势了,因此尾插数据的方式还能再进一步的优化,可以把尾插写成运算符重载,用'+='来实现尾插功能。

        优化尾插功能代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}void reserve(size_t n)//扩容函数{if (n > _capacity)//只有n大于_capacity才需要扩容{char* temp = new char[n + 1];if (_str != nullptr){strcpy(temp, _str);delete[] _str;}_str = temp;_capacity = n;}}void push_back(char ch)//尾插字符{if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size++] = ch;_str[_size] = '\0';}void append(const char* str)//尾插字符串{size_t len = strlen(str);if (_size+len > _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}strcpy(_str + _size, str);_size += len;}//两个运算符重载构成函数重载string& operator+=(char ch)//尾插字符--运算符重载{push_back(ch);//复用尾插函数return *this;}string& operator+=(const char* str)//尾插字符串--运算符重载{append(str);//复用尾插函数return *this;}const char* c_str(){return _str;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小};
}int main()
{ZH::string st1;st1 += 'h';st1 += 'e';st1 += 'l';st1 += 'l';st1 += 'o';st1 += " world";cout << st1.c_str() << endl;return 0;
}

         运行结果:

7、resize

void resize(int n,char ch='\0')
//n表示期望字符串的长度,如果n>_capacity则会扩容
//当n大于_size时,后面n-_size个空间赋予ch值
//n小于_size则表示字符串长度从_size缩小了n,但是空间不会缩容

        resize的作用是控制字符串的长度,可能会改变_size也可能会改变_capacity,注意的是:resize会增大空间但是不会缩小空间,原因就是缩小空间的代价太大,需要重新开辟一块空间然后拷贝数据,因此reszie缩小字符串长度时不会减小该字符串所在的空间。具体代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}void reserve(size_t n)//扩容函数{if (n > _capacity)//只有n大于_capacity才需要扩容{char* temp = new char[n + 1];if (_str != nullptr){strcpy(temp, _str);delete[] _str;}_str = temp;_capacity = n;}}void resize(int n,char ch='\0'){if (n < _size)//当n小于_size则更新_size并且直接将该位置的元素赋\0{_size = n;_str[_size] = '\0';}else//n大于_size时,_size后面的_size-n个值赋ch{if (n > _capacity)//n大于当前空间的容量则需要扩容{reserve(n);}while (_size!=n){_str[_size] = ch;_size++;}_str[_size] = '\0';}}const char* c_str(){return _str;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小};
}int main()
{ZH::string st1("hello world");ZH::string st2("hello world");st1.resize(20, 'x');st2.resize(5, 'x');cout << st1.c_str() << endl;cout << st2.c_str() << endl;return 0;
}

        运行结果:

        通过调试可以发现resize缩小字符串长度时,并没有对其空间容量进行缩小:

8、中间插入数据、删除数据

8.1 插入数据

        中间插入数据时要注意扩容问题,并且从某个位置插入时,原本该位置的数据要往后挪动,具体示意图如下:

        某个位置插入数据的代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}void reserve(size_t n)//扩容函数{if (n > _capacity)//只有n大于_capacity才需要扩容{char* temp = new char[n + 1];if (_str != nullptr){strcpy(temp, _str);delete[] _str;}_str = temp;_capacity = n;}}void Insert(size_t pos, char ch)//插入字符{assert(pos <= _size);//判断位置是否合规if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size + 1;//从此处刚好可以把\0拷贝过去while (end > pos){//采用的是指向位置的前一个位置的元素拷贝给指向位置_str[end] = _str[end - 1];end--;}_str[end] = ch;_size++;}void Insert(size_t pos, const char* str)//插入字符串{assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end >= pos + len){//采用的是指向位置的前len个位置的元素拷贝给指向位置_str[end] = _str[end - len];end--;}strncpy(_str + pos, str, len);_size = _size + len;}const char* c_str(){return _str;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小};
}int main()
{ZH::string st1("hello world");st1.Insert(3, 'z');st1.Insert(3, "aaa");cout << st1.c_str() << endl;return 0;
}

         运行结果:

8.2 删除数据

void erase(size_t pos, size_t n = npos)
//删除pos位置数据,n表示从pos位置起要删除n个数据
//npos表示-1,-1的正整数表示四十多亿,其实就是表示从pos位置开始后面数据全部删除

        npos可以用静态成员来定义,因为静态成员属于所有对象,属于整个类,删除的物理逻辑也和插入的物理逻辑相似,即删除一个数据后,后面的数据往前面挪动。

        删除数据的代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}void reserve(size_t n)//扩容函数{if (n > _capacity)//只有n大于_capacity才需要扩容{char* temp = new char[n + 1];if (_str != nullptr){strcpy(temp, _str);delete[] _str;}_str = temp;_capacity = n;}}void erase(size_t pos, size_t n = npos)//删除pos位置数据,n表示从pos位置起要删除n个元素{assert(pos <= _size);//判断要删除的位置是否合格if (n == npos || n + pos >= _size)//如果用的是缺省值nops,则表示从该位置起后面全部删除{_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + n);//用拷贝的形式实现删除_size -= n;}}const char* c_str(){return _str;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小static size_t npos;//定义一个静态成员变量};
}size_t ZH::string::npos = -1;//-1的正整数表示一个40多亿的数int main()
{ZH::string st1("hello world");ZH::string st2("hello world");st1.erase(3, 7);//从位置3开始删除7个元素st2.erase(3);//从位置3开始后面全部删除cout << st1.c_str() << endl;cout << st2.c_str() << endl;return 0;
}

        运行结果:

9、查找数据

        逻辑就是遍历字符串,找到了就返回该元素的下标,没找到可以返回npos,示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}size_t find(char c, size_t pos = 0)//查找字符{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == c){return i;//找到了返回该元素的下标}}return npos;//没找到返回npos}size_t find(const char* str, size_t pos = 0)//查找字符串{assert(pos < _size);char* poi = strstr(_str + pos, str);//复用strstr函数if (poi != nullptr)return poi - _str;//找到了通过指针-指针得到整数返回该数值elsereturn npos;//没找到返回npos}const char* c_str(){return _str;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小static size_t npos;//定义一个静态成员变量};
}size_t ZH::string::npos = -1;//-1的正整数表示一个40多亿的数int main()
{ZH::string st1("hello world");cout << st1.find('l') << endl;cout << st1.find("wor") << endl;cout << st1.c_str() << endl;return 0;
}

        运行结果:

10、打印对象(流插入、流提取)

        以上打印字符串用的方法都是传首地址打印方式,可以对流插入、流提取符号:‘<<'和'>>'进行运算符重载,就可以直接使用‘<<'和'>>'打印对象。

        代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace ZH//把string放在命名空间中,放在与库里的string重名
{class string{public:string(const char* str = "")//构造函数初始化对象:_size(strlen(str)){_capacity = _size;//实际上会给\0开一个空间,但是capacity不记录该空间_str = new char[_capacity + 1];strcpy(_str, str);}void reserve(size_t n)//扩容函数{if (n > _capacity)//只有n大于_capacity才需要扩容{char* temp = new char[n + 1];if (_str != nullptr){strcpy(temp, _str);delete[] _str;}_str = temp;_capacity = n;}}void push_back(char ch)//尾插字符{if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size++] = ch;_str[_size] = '\0';}void append(const char* str)//尾插字符串{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}//两个运算符重载构成函数重载string& operator+=(char ch)//尾插字符--运算符重载{push_back(ch);//复用尾插函数return *this;}string& operator+=(const char* str)//尾插字符串--运算符重载{append(str);//复用尾插函数return *this;}void clear(){_str[0] = '\0';_size = 0;}const char* c_str()const{return _str;}~string()//析构函数,释放_str申请的空间{delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;//在堆上开辟一块空间用于存放字符串size_t _size;//记录字符串的总字符数size_t _capacity;//记录所开辟空间的大小static size_t npos;//定义一个静态成员变量};ostream& operator<<(ostream& out, const string& str)//流插入{cout << str.c_str()<< endl;return out;}istream& operator>>(istream& in, string& str)//流提取{str.clear();//每次输入数据的时候把之前数据清空char ch;ch = in.get();size_t i = 0;char buff[128];//用buff作为一个缓冲区的概念,减少每次+=字符串的消耗while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[128] = '\0';//因为是一个字符的往buff里面输入,因此buff是没有\0的,要手动添加str += buff;//数组满了就一次性+=到stri = 0;}ch = in.get();}if (i != 0)//把数组剩余的数据+=到str中{buff[i] = '\0';str += buff;}return in;}
}size_t ZH::string::npos = -1;//-1的正整数表示一个40多亿的数int main()
{ZH::string st1("ssssssssssssssssssssssssss");cin >> st1;cout << st1 << endl;return 0;
}

        运行结果:

        这里注意库里面的流提取是每次输入新的数据时,旧的数据会直接清空,因此每次调用流提取函数时会先调用clear()函数清空之前的数据。

结语:

        以上就是关于string类的实现和讲解,只有了解了string底层是如何实现的,才能帮助我们进一步了解和使用string类。最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/597288.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos7部署minio单机版

一、目标 在centos7上部署minio单机版 二、centos7部署minio 1、下载minio mkdir /usr/local/minio cd /usr/local/minio wget https://dl.minio.io/server/minio/release/linux-amd64/minio chmod x minio 2、新建minio存储数据的目录 mkdir -p /data/minio/data3、新建…

sql语句条件查询,模糊查询

一.按条件表达式筛选 #案例1&#xff0c;查询工资>12000的员工信息 SELECT * FROM employees WHERE salary>12000; #案例2&#xff0c;查询部门编号不等于90号的员工名和部门编号SELECT last_name,department_idFROM employeesWHEREdepartment_id<>90;二 按逻辑表…

ASP.NETCore WebAPI 入门 杨中科

ASP.NETCore WebAPI入门1 回顾 mvc开发模式 前端代码和后端代码是混在一个项目之中 WEB API 1、什么是结构化的Http接口。Json。 2、Web API项目的搭建。 3、Web API项目没有Views文件夹。 4、运行项目&#xff0c;解读代码结构。 5、【启用OpenAPI支持】→>swagger,在界…

Spring的IOC解决程序耦合

目录 1.配置项目 1.1配置pom.xml 1.2Spring常用功能的Jar包依赖关系 1.3简单代码 2.IOC 2.1.applicationContext.xml 2.2.测试 3.DI 3.1概述 3.2.构造函数注入 3.3set方法注入 3.4自动注入 3.5注入集合类型的属性 1.配置项目 1.1配置pom.xml <?xml version&…

【算法每日一练]-动态规划(保姆级教程 篇14) #三倍经验 #散步 #异或和 #抽奖概率

目录 今日知识点&#xff1a; 金字塔的正反dp两种方案&#xff0c;转移方程取决于dp的具体含义 取模实现循环走m步回到原点的方案 在统计上升子序列的时候使用最小结尾元素进行标记&#xff0c;一举两得 将亏本的概率转换各种情况的方案&#xff0c;然后统计亏本的情况的方…

LeetCode——2487. 从链表中移除节点

通过万岁&#xff01;&#xff01;&#xff01; 题目&#xff1a;给你一个链表&#xff0c;然后让你从链表中移除一些节点&#xff0c;移除的规则就是我们选择的这个节点在原链表中往右不能有比这个节点大的值。思路&#xff1a;这个题我最开始以为是双指针&#xff0c;然后找…

数字孪生技术详解

在线工具推荐&#xff1a;3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 数字孪生技术正在迅速彻底改变企业的运营方式。借助数字孪生技术&#xff0c…

Lazada商品详情API(lazada.item_get)获取商品的图片信息

使用Lazada商品详情API&#xff08;lazada.item_get&#xff09;获取商品的图片信息&#xff0c;首先确保你已经注册了Lazada开发者账号并获取到了API密钥。下面是一个示例代码&#xff0c;展示如何使用Python调用该API并获取商品的图片信息&#xff1a; import requests im…

opengl和directx中,渲染管线是什么?

在opengl 3D画图&#xff08;渲染或图像处理&#xff09;中&#xff0c;很多人都围绕着一个pipeline的词做很多解释&#xff0c;似乎明白这个词的含义成了入门必须要领悟的一道门槛。但实际上呢&#xff1f; 这都是因为翻译错误搞得大家非要解释一番的。好好的翻译工具不用&am…

【Nginx】在线安装与离线安装

目录 1、下载nginx news 1.2、 安装包 2、 在线安装 第一步&#xff0c;安装pcre 第二步&#xff0c;安装openssl 、zlib 、 gcc 依赖 第三步&#xff0c;安装nginx 第四步&#xff0c;关闭防火墙&#xff0c;访问nginx ​编辑 3、 离线安装 第一步 安装pcre 第二步…

LeetCode 1531. 压缩字符串 II【动态规划】2575

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

Java SE 三个基本注解(JDK内置)+四个元注解

使用注解(Annotation)时要在前面加符号&#xff0c;注解可以当作一个修饰符来修饰他支持的修饰的元素 Override - 重写&#xff0c;该注解只能用于方法 Deprecated - 已弃用&#xff0c;用在程序元素上&#xff0c;如某个类上或者某个方法上 SuppressWarnings - 抑制编译器警告…

【KingbaseES】实现MySql函数Median

本方法只支持在聚合函数窗口中调用 不支持在GROUP BY中使用&#xff0c;使用plsql写的玩意新能都会稍微差一些 建议使用原生方法修改 CREATE OR REPLACE FUNCTION _final_median(numeric[])RETURNS numeric AS $$SELECT AVG(val)FROM (SELECT valFROM unnest($1) valORDER BY …

CSS的特性与简便写法

<!DOCTYPE html> <html> <head> <meta charset"UTF-8" /> <title>CSS的三大特性</title> <style> /* 子级继承父级的 继承性*/ body{ font-size: 50px; color: red; } /* 层叠性 */ /* 前面的红色会被后面的蓝色覆盖&a…

服务器感染了.kann勒索病毒,如何确保数据文件完整恢复?

导言&#xff1a; 勒索病毒成为当前网络安全领域的一大威胁。.kann勒索病毒是其中的一种变种&#xff0c;对用户的数据造成了极大的威胁。本文91数据恢复将介绍.kann勒索病毒的特征、应对策略以及预防措施&#xff0c;以帮助用户更好地保护个人和组织的数据安全。当面对被勒索…

JavaScript常用事件的类型演示

目录 (一).表单事件(二).UI事件(四).鼠标事件(五).键盘事件 (一).表单事件 1).submit:提交 2).reset&#xff1a;重置 3).change&#xff1a;内容改变且失去焦点时触发 4).input&#xff1a;内容改变时触发&#xff08;兼容性不好&#xff09; 注&#xff1a; submit和res…

vue中动态出来返回的时间秒数,在多少范围显示多少秒,多少范围显示分,小时等等

在Vue中&#xff0c;你可以使用计算属性&#xff08;computed property&#xff09;或过滤器&#xff08;filter&#xff09;来根据动态返回的时间秒数来显示不同的时间单位&#xff0c;比如秒、分、小时等等。 下面是一个使用计算属性的示例&#xff1a; <template>&l…

【Python小游戏】消消乐丨喜羊羊与灰太狼(完整代码)

文章目录 写在前面喜羊羊与灰太狼PyGame入门消消乐注意事项写在后面写在前面 本期内容:基于pygame实现喜羊羊与灰太狼版消消乐小游戏 实验环境 python3.11及以上pycharmpygame安装pygame的命令: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame喜羊羊与灰…

毛虫目标检测数据集VOC格式550张

毛虫&#xff0c;一种令人惊叹的生物&#xff0c;以其独特的外貌和习性&#xff0c;成为了自然界中的一道亮丽风景。 毛虫的外观非常特别&#xff0c;身体呈圆柱形&#xff0c;表面覆盖着许多细小的毛发&#xff0c;这使得它们在叶子上伪装得非常好。它们的头部有一对坚硬的颚…

力扣第一百道题,记录一下——x 的平方根

给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被 舍去 。 注意&#xff1a;不允许使用任何内置指数函数和算符&#xff0c;例如 pow(x, 0.5) 或者 x ** 0.5 。 很容易想到用二分…