网站pc和手机端,房地产市场现状分析2022,wordpress访问很慢吗?,苏州市建设厅网站首页前言
string类一直都是C的经典问题#xff0c;之前的文章已经对string类做了一个基本的介绍#xff08;string类的基本常用接口#xff09;#xff0c;为了更好理解string类的功能#xff0c;此篇文章将手把手教你带你手搓模拟实现string类#xff0c;快来一起学习吧的经典问题之前的文章已经对string类做了一个基本的介绍string类的基本常用接口为了更好理解string类的功能此篇文章将手把手教你带你手搓模拟实现string类快来一起学习吧 目录
一、 构造/析构/拷贝构造/赋值
1、 构造函数
1️⃣最初版本
❓【问题】❓
【解答】
❓【问题】❓
【解答】
2️⃣改进版本
3️⃣优化版本
2、析构函数
3、拷贝构造函数
4、operator
二、 遍历
1、operate[ ]
2、iterator 3、范围 for
三、比较
1、 operate
2、operator
3、operator
4、operator
5、operator 6、operator 四、增加
1、push_back
2、append 3、operator
4、insert
1插入一个字符
2插入一串字符
五、改操作
1、reserve
2、resize
3、erase
4、clean
5、swap
6、find
1查找一个字符
2查找字符串
六、流插入和流提取
1、流插入
2、流提取
七、完整代码 一、 构造/析构/拷贝构造/赋值
为了和标准库分开我们可以将我们需要模拟实现的string类用另一个命名空间封装起来。 1、 构造函数
1️⃣最初版本
我们很容易想到下面这种方式 但是在运行过程中会出现一系列问题
❓【问题】❓
不能直接把 str 赋值给 _str .
【解答】 在string类中str 是被const修饰的这是库里规定的不能改变。给成员变量 char*_str 加上 const 可以暂时解决这个问题但会使后期无法对字符串进行扩容。因此解决方法是开辟一块新的空间然后将 str 的数据拷贝到_str 中。 ❓【问题】❓
无参构造函数运行错误 【解答】 打印 s1,会调用无参构造函数cout 流插入是自动识别类型识别为 const char*他不是去打印这个指针是打印这个字符串‘\0’之前的数字就会对空指针进行解引用。因此解决方法是不在_str 中放入空指针而是开辟一个空间放入‘\0’; 2️⃣改进版本 3️⃣优化版本
利用构造函数的缺省值 2、析构函数 要与new[ ]对应用delete[ ]释放空间。 然后将指针置空大小容量置0。 3、拷贝构造函数 系统会默认生成拷贝构造函数 系统默认生成的浅拷贝两个指向同一块空间会导致两个问题改变一个会影响另外一个一块空间会被析构两次。因此我们需要深拷贝开辟一块和原来一样大的空间将数据拷贝过来。更多细节知识可以参考【拷贝构造函数】
4、operator 赋值是两个已经存在的对象将其中一个对象拷贝给另一个对象。从内存大小上看有三种情况两个对象的内存差不多大直接可以将内容拷贝过去赋值对象的内存比较大直接将内容拷贝过去但是可能造成空间的浪费赋值对象的内存比较小拷贝内容需要对其扩容。 因此我们可以直接开辟一块跟要赋值对象s一样大的空间tmp然后将赋值对象的值拷贝到tmp里。将被赋值对_str象的空间释放。最后将tmp赋给_str.将_str对象的大小和容量都与s对象一致。
二、 遍历
1、operate[ ]
需要两个版本编译器可以自动识别。
2、iterator
iterator其实在string里来说本质上可以看成一个指针类型。我们在这里自己定义一个iterator。定义完iterator类型后就可以写begin()和end()了。begin返回的是指向开头位置的迭代器end返回的是指向最后一个字符的下一个位置。迭代器也需要分为const和非const版本。 3、范围 for
范围 for 遍历的底层逻辑是迭代器因此定义迭代器之后可以直接使用。
【运行结果】
三、比较
1、 operate
比较大小是依次比较字母的 ascll 码值。
2、operator
3、operator
4、operator
5、operator
6、operator
四、增加
1、push_back 首先需要判断内存是否需要扩容扩容可以自己修改本文选择是两倍扩容。然后直接将字符放在最后size不要忘记补上‘\0’;
2、append 首先需要判断内存是否需要扩容。然后将 str 字符串加在原字符串后面strcpy 会把‘\0’ 一起拷贝过去因此不需要单独加上。 3、operator 的底层逻辑依旧是push_back 和 append。
4、insert
1插入一个字符 首先需要判断内存大小把pos后的字符从前往后挪一个然后把指定字符放入 pos。最后更新size需要注意头插end容易产生越界的问题本文采用的解决方案是把end往后移一位。
2插入一串字符
五、改操作
1、reserve 功能是预留空间主要用来扩容。当数据实际大小大于容量时就要开始扩容。原理就是在异地开辟一块空间多开一个字符用来存放 ‘\0’然后将原字符拷贝过来释放原空间。将空间和容量再次赋值给原字符串。
2、resize resize 和 reserve 的区别就是 resize 会扩容之后初始化如果规定初始化内容是在原字符串的后面加上规定的初始化内容。改变的仅仅是sizecapacity不会改变。resize n 有三种情况 ① n size 直接删除数据保留前n个 ② size n capacity 向后插入n-size个字符 ③ n capacity 扩容 ncapacity 不变
3、erase erase()删除某个位置len个字符len给了缺省值npos也就是不写长度时默认从pos位置一直删除到尾。所以有两种情况 ①poslen size ,删除pos后的所有值直接将 pos 位置的值改成“\0”; ②poslen size时把poslen后面的值拷贝到pos后然后调整size即可。 4、clean
只需要将第一个位置上修改成’\0’大小修改成0. 5、swap 6、find
1查找一个字符 find()可以从某个位置开始查找某个字符返回改字符的位置。 首先需要判断pos位置是否合法。直接利用遍历从pos位置开始查找该字符ch如果找到直接返回该下标如果没有找到则返回npos。 2查找字符串 返回该字符串的位置。 首先需要判断pos位置合法性直接使用strstr来查找字符串找到返回指向该字符起始位置的指针。指针-指针等于长度所以p减去起始位置就是p的位置。 六、流插入和流提取
1、流插入 流插入cout利用运算符重载要注意这个函数不能写成成员函数因为this指针会抢占左操作数而左操作数应该是流插入cout。所以必须写在类外不能写在类里。写在类外的话想要访问类里的私有成员就得需要使用友元而这里可以不需要使用友元就可以访问私有成员那就是一个一个字符打印。 2、流提取 ①cin和scanf当遇到空格或换行都会停止读取。 ②本文使用get()函数因为不管是换行还是空格都会读取。 ③每次读取之前都需要将缓冲区内容清空不然下一次读取就会将上一次的内容也读取下来。 ④这里如果每次都读取一个字符会很麻烦因为会不断的扩容如果提取的字符很长就会从小到大不断扩容。所以这里采取的是将提取的字符放入一个数组里当提取部分或者全部提取之后再放进去。这样就可以减少扩容次数了。注意最后一位要放入’\0’. 七、完整代码
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#includeassert.h
#includestringnamespace zhou
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str_size;}iterator begin() const{return _str;}iterator end() const{return _str _size;}//无参的构造函数/*string():_str(new char[1]),_size(0),_capacity(0){ _str[0] \0;}*///带参的构造函数//str是被const修饰的是库里面决定的不能改变//string(const char* strnullptr) 不可以strlen遇到\0才停止遇到空指针会崩溃 //string(const char* str \0) 不可以类型不匹配左边是char类型的//string(const char* str \0) //可以是常量字符串strlen是0可以正常运算。string(const char* str ) //可以不写默认是\0:_size(strlen(str)){_capacity _size;_str new char[_capacity 1];strcpy(_str, str);}//返回c形式的字符串const char* c_str(){return _str;}string(const string s):_size(s._size), _capacity(s._capacity){_str new char[s._capacity 1];strcpy(_str, s._str);}//无 const 修饰char operator[](size_t pos){assert(pos _size);return _str[pos];}//有 const 修饰const char operator[](size_t pos) const{assert(pos _size);return _str[pos];}size_t size(){return _size;}//赋值string operator(const string s){if (this ! s){char* tmp new char[s._capacity 1];strcpy(tmp, s._str);delete[]_str;_str tmp;_size s._size;_capacity s._capacity;}return *this;}//不修改成员变量数据的函数最好都加上constbool operator(const string s) const{return strcmp(_str, s._str) 0;}bool operator(const string s) const{return strcmp(_str, s._str) 0;}bool operator(const string s) const{//return *this s || *this s;return *this s || s *this;}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);}void reserve(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){reserve(n);}size_t i _size;while (i n){_str[i] ch;i;}_size n;_str[_size] \0;}}void push_back(char ch){//要判断内存if (_size 1 _capacity){reserve(2 * _capacity);}_str[_size] ch;_size;//不要忘\0_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;}string insert(size_t pos, char ch){assert(pos _size);if (_size 1 _capacity){reserve(2 * _capacity);}//问题代码会发现头插时会崩溃/*size_t end _size;while (end pos){_str[end 1] _str[end];end--;}*/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* str){size_t len strlen(str);assert(pos _size);if (_size len _capacity){reserve(_size len);}size_t end _size len;while (end pos len-1){_str[end] _str[end-len];end--;}strncpy(_str pos, str,len);_size len;return *this;}string erase(size_t pos, size_t len npos){assert(pos _size);if (len npos || pos len _size){_str[pos] \0;_size pos;}else{strcpy(_str pos, _str pos len);_size - len;}return *this;}void swap(string s){std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);}size_t find(char ch, size_t pos 0){assert(pos _size);for (size_t i pos; i _size; i){if (_str[i] ch){return i;}}return npos;}size_t find(const char* str, size_t pos 0){assert(pos _size);char* p strstr(_str pos, str);if (p nullptr){return npos;}else{return p - _str;}}void clear(){_str[0] \0;_size 0;}~string(){delete[] _str;_str nullptr;_capacity _size 0;}private:char* _str;size_t _size;size_t _capacity;static const size_t npos;};const size_t string::npos -1;ostream operator(ostream out, const string s){for (auto ch : s){out ch;}return out;}istream operator(istream in, string s){s.clear();char ch in.get();char buff[128];size_t i 0;while (ch ! ch ! \n){buff[i] ch;if (i 127){buff[127] \0;s buff;i 0;}ch in.get();}if (i ! 0){buff[i] \0;s buff;}return in;}void test_string1(){string s1;string s2(hello world);cout s1.c_str() endl;cout s2.c_str() endl;}void test_string2(){string s(hello world);//下标遍历for (size_t i 0; i s.size(); i){couts[i];}cout endl;//iterator遍历string::iterator it s.begin();while (it s.end()){cout *it;it;}cout endl;//范围for for (auto ch : s){cout ch;}}}