🏆
个人主页
:
企鹅不叫的博客
🌈
专栏
⭐️
博主码云gitee链接:
代码仓库地址
⚡若有帮助可以【
关注
+
点赞
+
收藏
】,大家一起进步!
💙系列文章💙
【初阶与进阶C++详解】第二篇:C&&C++互相调用(创建静态库)并保护加密源文件
【初阶与进阶C++详解】第三篇:类和对象上(类和this指针)
【初阶与进阶C++详解】第四篇:类和对象中(类的六个默认成员函数)
【初阶与进阶C++详解】第五篇:类和对象下(构造+static+友元+内部类
【初阶与进阶C++详解】第六篇:C&C++内存管理(动态内存分布+内存管理+new&delete)
【初阶与进阶C++详解】第七篇:模板初阶(泛型编程+函数模板+类模板+模板特化+模板分离编译)
文章目录
💎一、标准库中的string类
🏆2.1string类
#include,不用加.h是为了和C语言区分,
必须包含#include头文件以及using namespace std
🏆2.2string类接口
2.2.1string类对象的常见构造
void Teststring() { //以下字符串都默认有"\0" string s1; // 构造空的string类对象s1 string s2("hello bit"); // 用C格式字符串构造string类对象s2 string s3(s2); // 拷贝构造s3 string s4 = s2;//拷贝构造 string s5("abcdefg", 3);//初始化前3个 string s6(100,"A");//用100个A初始化 }
2.2.2string类对象的容量操作
函数名称 功能说明 size(重点) 返回字符串有效字符长度 length 返回字符串有效字符长度 capacity 返回空间总大小 empty (重点) 检测字符串释放为空串,是返回true,否则返回false clear (重点) 清空有效字符,不改变底层空间的大小 reserve (重点) 为字符串预留空间,可以填充 resize (重点) 修改字符个数上线,并且可以添加字符 reserve和resize:
reserve (size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:reserve (n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间,不填c的话,默认是给0。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变,
reserve只修改capacity大小,不修改size大小,resize既修改capacity大小,也修改size大小
。
2.2.3string类对象的访问及遍历操作
函数名称 功能说明 operator[] (重点) 返回pos位置的字符,const string类对象调用 begin+ end begin获取第一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 rbegin + rend rbegin获取最后一个字符的迭代器 + rend获取第一个字符位置的迭代器 范围for C++11支持更简洁的范围for的新遍历方式 template<class T = char> class basic_string { pubulic: basic_string(const T* str) { //开空间存储,方便增删查改 size_t len = strlen(str); _str = new T(len + 1);//预留\0 strcpy(_str, str); } //减少拷贝,可以读,可以修改 T& operator[](size_t pos) { return _str[pos]; } private: const T* _str; size_t _size; size_t _capacity; }; int main () { string s("hello world"); //1.下标+[] for (int i = 0; i < s.size(); ++i) { //将每个字符加一 s[i] += 1; cout << s[i] << " "; } //2.1 正向迭代器(类似指针,链表需要,区间是左闭右开,可读可写) //可以使用auto简化 //auto it = s.begin(); string::iterator it = s.begin(); while (it != s.end()) { //将每个字符加一 (*it) += 1; ++it; } //2.2 反向迭代器() string::reverse_iterator rit = s.rbegin(); while (rit != s.rend()) { //将每个字符加一 (*rit) += 1; ++rit; } //2.3 正向const迭代器(此时迭代器只能读,不能写) string::const_iterator cit = s.begin(); while (cit != s.end()) { //此时不能写 //(*cit) += 1; ++cit; } //2.4 反向向const迭代器(此时迭代器只能读,不能写) // string::const_reverse_iterator crit = s.begin(); //3.范围for for (auto ch : s) { cout << ch << endl; } cout << endl;; return 0; }
2.2.4string类对象的修改操作
函数名称 功能说明 c_str(重点) 返回C格式字符串 find+npos(重点) 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 rfind 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 substr 在str中从pos位置开始,截取n个字符,然后将其返回 int main() { //1.c_str //返回一个指向数组的指针,该数组包含一个以空字符结尾的字符序列(即一个 C 字符串),为了和C相适应 //下面打印出来是一样的 string str("hello"); cout << str << endl; cout << str.c_str() << endl; //2.find在字符串中查找内容 string file("file.cpp"); size_t found = file.find('.'); //npos是static const size_t npos = -1;无符号为最大值 //从found开始,直到npos结束 if (found != string::npos) { //3.substr截取字符串 string suffix = file.substr(found, file.size()-found); cout << suffix << endl; } }
💎二、string类的模拟实现
🏆3.1string的基本函数
class string { //实现深浅拷贝 public: string(const char* str) :_str(new char[strlen(str)+1]) { strcpy(_str, str); } ~string() { if (_str) { delete[]_str; } } const char* c_str() const { return _str; } char& operator[](size_t pos) { assert(pos < strlen(_str)); return _str[pos]; } size_t size() { return strlen(_str); } private: char* _str; }; void Teststring() { string s1("hello!"); string s2(s1); }
🏆3.2string类模拟实现
class string { //实现深浅拷贝 public: //浅拷贝,不能将str直接初始化_str,str是常量字符串直接给_str会造成权限放大 //构造函数,记得给缺省值 string(const char* str = "") :_size(strlen(str)) , _capacity(_size) { //所以另外给_str开一段空间,多一个给'\0' _str = new char[_capacity + 1]; strcpy(_str, str); } //无参默认构造,默认给\0 string() :_size(0) ,_capacity(_size) { _str = new char[1]; _str[0] = '\0'; } //深拷贝 //传统写法-拷贝构造 string(const string& s) :_size(strlen(s._str)) ,_capacity(_size) { //开辟空间,然后将开辟的空间地址给_str _str = new char[_capacity + 1]; strcpy(_str, s._str); } void swap(string& s) { //交换_str和tmp._s,就相当于给this->_str开辟了一块空间,当拷贝函数结束,tmp就会被自动释放 std::swap(_str, s._str);//调用std中函数 std::swap(_size, s._size); std::swap(_capacity, s._capacity); } //现在写法-利用构造函数构造一个临时对象,然后利用swap将临时对象换给新的对象 //S2(S1) string(const string& s) : _str(nullptr)//置空,开始_str是个随机数,交给tmp,_str后,释放会引起问题 , _size(0) , _capacity(0) { string tmp(s._str);//利用构造函数给tmp对象开辟一块空间 //tmp与this交换 swap(tmp); } //析构函数 ~string() { if (_str) { delete[]_str; _str = nullptr; _size = _capacity = 0; } } const char* c_str() const { return _str; } char& operator[](size_t pos) { assert(pos < strlen(_str)); return _str[pos]; } //用引用返回防止深拷贝 string& operator=(const string& s) { //防止自己给自己赋值 if (this != &s) { 传统写法 首先创建一个tmp用来开辟所需空间,防止开辟失败导致原数据丢失 //char* tmp = new char(s._capacity + 1); //strcpy(tmp, s._str); 不知道传过来的空间有多大,从而无法确定应该准备多大空间,所以删除原来的空间,重新计算所需空间大小 //delete[]_str; //_str = tmp; //_size = s._size; //_capacity = s._capacity; //新写法 string tmp(s._str); swap(tmp); } return *this; } //更加简洁的写法 //s是传值传参 string& operator=(string s) { swap(s); return *this; } size_t size()const { return _size; } size_t capacity()const { return _capacity; } string& operatpr+=(char ch) { push_back(ch); return *this; } void reseve(size_t n) { if (n > _capacity) { char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[]_str; _str = tmp; _capacity = n; } } void resize(size_t n, char ch = '\0') { if (n < _size) { _size = n; _str[_size] = '\0'; } else { if (n > _capacity) { reseve(n); } for (size_t i = _size; i < n; ++i) { _str[i] = ch; } _size = n; _str[_size] = '\0'; } } //尾插一个字符 void push_back(char ch) { //判断是不是满了 if (_capacity == _size) { reseve(_capacity == 0 ? 4 : _capacity * 2); } _str[_size] = ch; ++_size; _str[_size] = '\0'; } //尾插一个字符串 void append(const char* str) { size_t len = _size + strlen(str); if (len > _capacity) { reserve(len); } strcpy(_str + _szie, str); _size = len; } ostream& operator<<(ostream& out, const string& s) { for (auto ch : s) { out << ch; } return out; } istream& operator>>(istream& in, const string& s) { char ch; ch = in.get(); char buff[128] = {'\0'}; size_t i = 0; while (ch != ' ' || ch != '\0') { buff[i++] = ch; if (i == 127) { s += buff; memset(buff, '\0', 128); i = 0; } ch = in.get(); } s += buff; return in; } bool operator<(const string& s1, const string& s2) { return strcmp(s1.c_str(), s2.c_str()) < 0; } bool operator==(const string& s1, const string& s2) { return strcmp(s1.c_str(), s2.c_str()) == 0; } bool operator>(const string& s1, const string& s2) { return strcmp(s1.c_str(), s2.c_str()) > 0; } bool operator<=(const string& s1, const string& s2) { return strcmp(s1.c_str(), s2.c_str()) <= 0; } bool operator>=(const string& s1, const string& s2) { return strcmp(s1.c_str(), s2.c_str()) >= 0; } bool operator!=(const string& s1, const string& s2) { return strcmp(s1.c_str(), s2.c_str()) != 0; } //插入一个字符 string& insert(size_t pos, char ch) { assert(pos < _size); if (_size == _capacity) { reserve(_capacity == 0 ? 4 : _capacity*2); } //不能用size_t,后面end减小的时候可能会变成-1 int end = _size; //end类型是int,pos类型是size_t,两者比较,int会整型提升unsinged int //如果pos是size_t的话,会死循环 while (end >= (int)pos) { _str[end+1] = _str[end]; end--; } _str[pos] = ch; ++_size; return *this; } //插入一个字符串 string& insert(size_t pos, const char* str) { assert(pos <= _size); size_t len = strlen(_size); if (len + _size > _capacity) { reserve(_size+len); } //往后挪动len长度 size_t end = _size+len; while (end >= pos+len) { _str[end] = _str[end-len]; --end; } strncpy(_str + pos, str, len); _size += len; return *this; } //删除 string& earse(size_t pos, size_t len == npos) { assert(pos < _size); //如果删除长度超过字符串长度,或者删除完毕了 if (len == npos || pos + len >= _size) { _str[pos] = '\0'; _size = pos; } //直接覆盖 else { size_t begin = pos + len; while (begin <= _size) { _str[begin-len] = _str[begin]; ++begin; } _string&size -= len; } return *this; } //从pos位置开始寻找一个字符,找到就范围字符位置,没找到就返回npos size_t find(char ch, size_t pos) { while (pos < _size) { if (_str[pos] == ch) { return pos; } pos++; } return npos; } //从pos位置开始找一段字符串,我们可以使用strstr字符串查找函数 size_t find(const char* str, size_t pos) { const char* p = strstr(_str+pos, str); if (p == nullptr) { return npos; } else { return p - _str; } } //void claer() private: char* _str; size_t _size;//有效字符的个数 size_t _capacity;//存储有效字符的空间 const static size_t npos;//这里是声明不能给缺省值 }; const static size_t string::npos = -1;
🏆3.3 to_string用法
功能:将数字常量转换为字符串
//下面将输入的i转化成字符串 int main() { int i; cin >> i; string s = to_string(i); }
🏆3.4 stoi用法
功能:将的字符串转化为十进制
stoi(字符串,起始位置,n进制),将 n 进制的字符串转化为十进制 示例: stoi(str, 0, 2); //将字符串 str 从 0 位置开始到末尾的 2 进制转换为十进制
🏆3.5深浅拷贝问题
string s1("hello world"); string s2(s1);
如果是浅拷贝
运行以上代码会报错,理由是s1和s2都指向同一个空间,到析构函数的时候,会调用两次析构函数,该控件会释放两次,为避免浅拷贝,我们要给新对象开辟一个空间进行深拷贝,
在上面模拟实现中有深拷贝实现