深入剖析String类的底层实现原理
嘿嘿,家人们,今天咱们来模拟实现string,好啦,废话不多讲,开干!
1:string.h
1.1:构造函数与拷贝构造函数
1.1.1:写法一
1.1.2:写法二(给缺省值)
1.2:赋值运算符重载与operatror[]获取元素
1.3:容量与迭代器
1.4:reserve与resize
1.5:清空与判断是否为空
1.6:push_back与append
1.7:insert
1.7.1:插入字符
1.7.2:插入字符串
1.8:erase
1.9:operator+=
1.9.1:添加字符
1.9.2:添加字符串
1.91:find
1.91.1:查找字符
1.91.2:查找字符串
1.92:substr与swap
1.93:非成员函数的重载
1.93.1:流插入与流提取的重载
1.93.2:其他非成员函数的重载
2:Test.cpp
2.1:构造函数与拷贝构造
2.1.1:测试写法一
2.1.2:测试写法二
2.2:测试赋值运算符重载与[]获取元素
2.3:测试迭代器与容量
2.4:测试reserve与resize
2.4.1:测试resize
2.4.2:测试reserve
2.5:测试push_back与append
2.6:测试insert
2.6.1:测试插入字符
2.6.2:测试插入字符串
2.7:测试erase
2.8:测试operator+=
2.9:测试find
2.91:测试substr与swap
2.92:测试流插入与流提取
2.92.1:第一版流提取
2.92.2:第二版流提取
2.93:测试其他非成员函数
3:知识补充
3.1:深拷贝与浅拷贝
3.1.1:浅拷贝
3.1.2:深拷贝
4:总代码
4.1:string.h
4.2:Test.cpp
1:string.h
1.1:构造函数与拷贝构造函数
1.1.1:写法一
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;//避免与库中的string冲突,因此使用命名空间
namespace MyString
{class string{public:string():_str(new char[1]), _size(0),_capacity(0){//空字符串只有\0,_str[0] = '\0';}//构造非空字符串string(const char * str)//开辟空间,多开辟个空间存放\0:_str(new char[strlen(str) + 1]){//开辟空间_capacity = _size = strlen(str);//拷贝数据strcpy(_str, str);}//拷贝构造函数//s2(s1),使用s1拷贝构造s2string(string & s){//开辟空间char* temp = new char[s._capacity + 1];_str = temp;_capacity = s._capacity;_size = s._size;//拷贝数据strcpy(_str, s._str);}//析构函数~string(){delete[] _str;_size = _capacity = 0;}private://定义成员变量并且给缺省值char* _str;int _size;int _capacity;};}
1.1.2:写法二(给缺省值)
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;//避免与库中的string冲突,因此使用命名空间
namespace MyString
{class string{public://string()// :_str(new char[1])// , _size(0)// ,_capacity(0)//{// //空字符串只有\0,// _str[0] = '\0';//}构造非空字符串//string(const char * str)// //开辟空间,多开辟个空间存放\0// :_str(new char[strlen(str) + 1])//{// //开辟空间// _capacity = _size = strlen(str);// //拷贝数据// strcpy(_str, str);//}//给缺省值,构造空字符串时使用缺省值string(const char* str = "")//开辟空间,多开辟个空间存放\0:_str(new char[strlen(str) + 1]){//开辟空间_capacity = _size = strlen(str);//拷贝数据strcpy(_str, str);}/*拷贝构造函数s2(s1),使用s1拷贝构造s2*/string(string & s){//开辟空间char* temp = new char[s._capacity + 1];_str = temp;_capacity = s._capacity;_size = s._size;//拷贝数据strcpy(_str, s._str);}//析构函数~string(){delete[] _str;_size = _capacity = 0;}private://定义成员变量并且给缺省值char* _str = nullptr;int _size = 0;int _capacity = 0;};}
1.2:赋值运算符重载与operatror[]获取元素
//s1 = s2 this---->s1 s2----->s//赋值运算符重载string & operator =(string & s){//开辟新空间char * temp = new char[s._capacity + 1];//释放旧空间delete _str;//拷贝数据strcpy(temp, s._str);//指向新空间_str = temp;_size = s._size;_capacity = s._capacity;return *this;}//T &,返回引用同样可以提高效率,有返回值的目的是为了支持连续赋值.char & operator[](size_t position){assert(position < _size);return _str[position];}//const成员函数,给const对象调用,与上面构成函数重载const char& operator[](size_t position) const{assert(position < _size);return _str[position];}
1.3:容量与迭代器
迭代器呢其实是一个类指针,因此我们在模拟实现的时候,直接通过指针来进行模拟即可~
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;//避免与库中的string冲突,因此使用命名空间
namespace MyString
{class string{typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}//设置成员函数const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}//capacitysize_t size(){return _size;}size_t capacity(){return _capacity;}private://定义成员变量并且给缺省值char* _str = nullptr;int _size = 0;int _capacity = 0;};}
1.4:reserve与resize
我们来回顾下reserve与resize的相关知识
1:reserve
- 比size小不变化
- 比size大但小于capacity也不发生变化
比capacity大才会进行扩容
因此我们只要判断是否比capacity大
2:resize
比size小则进行删除.
- 比size大但小于capacity会改变size,并用\0插入.
比capacity大,会先改变size的大小同时进行扩容.
//仅需判断是否比capacity大void reserve(size_t n){if(n > _capacity){//开辟新空间char* temp = new char[n + 1];//拷贝数据strcpy(temp, _str);//释放旧空间delete[] _str;//指向新空间_str = temp;_capacity = n;}}void resize(size_t n,char ch = '\0'){//比size小则会进行删除if (n <= _size){_str[n] = ch;}//比size大比capacity小会改变size,并且用\0插入else if( n > _size && n <= _capacity){_size = n;}//比capacity大, 会先改变size的大小同时进行扩容.else{//扩容reserve(n);for(size_t i = _size; i < _capacity ; i++){_str[i] = ch;}_size = n;}}
1.5:清空与判断是否为空
//只清除有效字符,不改变底层空间void clear(){_str[0] = '\0';_size = 0;}bool empty()const{return _size == 0;}
这里博主就不带着uu们测试啦,感兴趣的uu可以自己下去测试下~
1.6:push_back与append
//Modifiersvoid push_back(char ch){//检查容量if (_size == _capacity)//二倍扩容reserve(_capacity == 0 ? 4 : 2 * _capacity);_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char * str){assert(str);//求出添加的字符串的长度int length = strlen(str);//判断添加后的有效字符数量是否越过容量if (_size + length > _capacity)reserve(_size + length);//_str + _size的位置为\0strcpy(_str + _size, str);_size += length;}
1.7:insert
1.7.1:插入字符
string & insert(size_t position,char ch){assert(position <= _size);if (_size == _capacity)//二倍扩容reserve(_capacity == 0 ? 4 : 2 * _capacity);//从后向前挪动覆盖for(int i = _size - 1; i >= position ;i--){_str[i + 1] = _str[i];}_str[position] = ch;_size++;_str[_size] = '\0';return *this;}
1.7.2:插入字符串
string & insert(size_t position,const char * str){assert(position <= _size);int length = strlen(str);if (_size + length > _capacity)reserve(_size + length);//从后向前挪动覆盖//_size + length - 1表示挪动的字符串的重点,positon + length表示挪动的字符串的起点for(int i = _size + length - 1; i >= position + length; i--){_str[i] = _str[i - length];}//拷贝数据strncpy(_str + position, str, length);_size += length;_str[_size] = '\0';return *this;}
1.8:erase
void erase(size_t position = 0,size_t len = -1){//防止越界assert(position < _size);//len == -1:如果 len 为 -1,表示删除到字符串末尾。//len >= _size - pos,如果 len 超过从 pos 开始的剩余字符长度,同样认为是删除到末尾。if(len == -1 || len >= _size - position){_str[position] = '\0';_size = position;}else{//将 pos + len 后的字符串内容复制到 pos 位置,从而覆盖中间 len 长度的字符,实现删除操作strcpy(_str + position, _str + position + len);}}
1.9:operator+=
1.9.1:添加字符
string& operator+=(char ch){push_back(ch);return *this;}
1.9.2:添加字符串
string& operator+=(const char * str){append(str);return *this;}
1.91:find
1.91.1:查找字符
//添加const修饰,令const成员与非const成员均可调用size_t find(char ch, size_t pos = 0) const{for(size_t i = pos; i < _size;i++){if (_str[i] == ch)return i;}return -1;}
1.91.2:查找字符串
const char * c_str()const{return _str;}//添加const修饰,令const成员与非const成员均可调用size_t find(const char * str,size_t pos = 0 )const{const char* p = strstr(_str + pos, str);if(p != NULL){//指针 - 指针得到对应的首字符下标return p - _str;}return -1;}
1.92:substr与swap
string substr(size_t pos = 0, size_t len = -1){string substr;//len == -1:如果 len 为 -1,表示全部获取。//len > _size - pos,如果 len 超过从 pos 开始的剩余字符长度,同样认为是全部获取if(len > _size - pos || len == -1){for(size_t i = 0; i < _size; i++){substr += _str[i];}}else{for(size_t i = pos; i < pos + len;i++){substr += _str[i];}}return substr;}//s1.swap(s2)void swap(string & str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}
1.93:非成员函数的重载
1.93.1:流插入与流提取的重载
ostream& operator<<(ostream& _cout,MyString::string& str){for (auto ch : str){cout << ch;}return _cout;}istream& operator>>(istream& _cin, MyString::string& str){第一版:会导致空间的浪费//char ch;_cin是无法读到\n与' '的//ch = _cin.get();清空字符//str.clear();//while (ch != '\n' && ch != ' ')//{// str += ch;// ch = _cin.get();//}//return _cin;//第二版char ch;ch = _cin.get();//底层给个buffchar buff[128];size_t i = 0;//将原本的字符清空str.clear();//cin无法读取' '与'\n'while (ch != '\n' && ch != ' '){buff[i++] = ch;//最后一个字符设置为\0if(i == 127){buff[i] = '\0';str += buff;}//持续读取字符ch = _cin.get();}if(i > 0){buff[i] = '\0';str += buff;}return _cin;}
}
按照常规方式,流提取按照上面的第一版方式进行重载即可,但是,底层其实是给了buff,目的是: 防止空间的浪费,因为按照常规方式重载的话,那么在 扩容的时候一般是1.5倍或者2倍扩容,而通过给一个buff,能够最大程度地防止空间的浪费.
1.93.2:其他非成员函数的重载
//会先调用此swap,有现成的,吃现成的,不使用模版里面的swapvoid swap(string& x, string& y){x.swap(y);}//自定义类型传值传参会调用拷贝构造,因此需要传引用bool operator==(const MyString::string& str1, const MyString::string& str2){int result = strcmp(str1.c_str(), str2.c_str());return result == 0;}bool operator<(const MyString::string& str1, const MyString::string& str2){int result = strcmp(str1.c_str(), str2.c_str());return result < 0;}bool operator>(const MyString::string& str1, const MyString::string& str2){int result = strcmp(str1.c_str(), str2.c_str());return result > 0;}bool operator<=(const MyString::string& str1, const MyString::string& str2){return (str1 < str2) || (str1 == str2);}bool operator>=(const MyString::string& str1, const MyString::string& str2){return !(str1 < str2);}bool operator!=(const MyString::string& str1, const MyString::string& str2){return !(str1 == str2);}
2:Test.cpp
2.1:构造函数与拷贝构造
2.1.1:测试写法一
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestConstructionAndCopyConstruction()
{MyString::string s1;MyString::string s2("hello world");MyString::string s3(s2);
}
int main()
{TestConstructionAndCopyConstruction();return 0;
}
2.1.2:测试写法二
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestConstructionAndCopyConstruction()
{MyString::string s1;MyString::string s2("hello world");MyString::string s3(s2);
}
int main()
{TestConstructionAndCopyConstruction();return 0;
}
2.2:测试赋值运算符重载与[]获取元素
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestAssignmentOperatorOverloadingAndElementAccess()
{MyString::string s1;MyString::string s2("hello world");const MyString::string s3("hello Linux");s1 = s2;cout <<"s1[0]:>" << s1[0] << endl;cout <<"s3[6]:>" << s3[6] << endl;
}
int main()
{TestAssignmentOperatorOverloadingAndElementAccess();return 0;
}
2.3:测试迭代器与容量
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestIteratorAndCapacity()
{MyString::string s1;MyString::string s2("hello world");s1 = s2;for(auto & element : s2){cout << element << endl;}cout <<"s2.size()" << s2.size() << endl;cout <<"s2.capacity()" << s2.capacity() << endl;
}int main()
{TestIteratorAndCapacity();return 0;
}
2.4:测试reserve与resize
2.4.1:测试resize
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestResize()
{MyString::string str("hello world");//比size小则进行删除str.resize(10);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << endl;//比size大但小于capacity会用改变size,并用\0插入str.resize(13);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << endl;//比capacity大,会先改变size的大小同时进行扩容str.resize(20);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;
}int main()
{TestResize();return 0;
}
2.4.2:测试reserve
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestRserve()
{MyString::string str("hello world");cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << "reserver后" << endl;cout << endl;//比size小不变化str.reserve(10);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << endl;//比size大但小于capacity也不发生变化str.reserve(13);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << endl;//比capacity大才会进行扩容str.reserve(20);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;
}int main()
{TestRserve();return 0;
}
2.5:测试push_back与append
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestPushBackAndAppend()
{MyString::string str;str.append("hello world");str.push_back('x');
}int main()
{TestPushBackAndAppend();return 0;
}
2.6:测试insert
2.6.1:测试插入字符
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestInsert()
{MyString::string str("hello world");//在下标为5的位置插入字符hstr.insert(5, 'h');
}int main()
{TestInsert();return 0;
}
2.6.2:测试插入字符串
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestInsert()
{MyString::string str("hello world");//在下标为5的位置插入字符hstr.insert(5, 'h');str.insert(2, "Linux");
}int main()
{TestInsert();return 0;
}
2.7:测试erase
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestErase()
{MyString::string str("hello world");str.erase(2, 4);
}int main()
{TestErase();return 0;
}
2.8:测试operator+=
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void Test()
{MyString::string str("hello");str += ' ';str += "Linux";
}int main()
{Test();return 0;
}
2.9:测试find
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestFind()
{MyString::string str("hello Linux");cout << str.find('l', 2) << endl;cout << str.find("Linux", 2) << endl;
}int main()
{TestFind();return 0;
}
2.91:测试substr与swap
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestSwapAndSubstr()
{MyString::string s1("hello Linux");MyString::string s2("hello bit");MyString::string temp = s1.substr(6, 5);cout << "交换前" << endl;cout << "s1:>" << s1 << endl;cout << "s2:>" << s2 << endl;cout <<"temp:>" << temp << endl;s1.swap(s2);cout << "交换后" << endl;cout <<"s1:>" << s1 << endl;cout <<"s2:>" << s2 << endl;
}
int main()
{TestSwapAndSubstr();return 0;
}
2.92:测试流插入与流提取
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestStreamInsertionAndStreamExtraction()
{MyString::string s1;cin >> s1;cout << s1 << endl;cout << "s1.size:>" << s1.size() << endl;cout << "s1.capacity:>" << s1.capacity() << endl;
}
int main()
{TestStreamInsertionAndStreamExtraction();return 0;
}
2.92.1:第一版流提取
2.92.2:第二版流提取
2.93:测试其他非成员函数
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"
void TestOtherFunction()
{MyString::string s1("hello world");MyString::string s2("hello bit");swap(s1, s2);s1.swap(s2);cout << s1.c_str() << endl;cout << s2.c_str() << endl;cout << (s1 == s2) << endl;cout << (s1 >= s2) << endl;cout << (s1 > s2) << endl;cout << (s1 < s2) << endl;cout << (s1 <= s2) << endl;cout << (s1 != s2) << endl;
}int main()
{TestOtherFunction();return 0;
}
3:知识补充
3.1:深拷贝与浅拷贝
在讲深浅拷贝之前,我们来看一个现象
我们可以清晰地看到,s2与s3的地址一样,这是为什么呢,因为博主将显示定义的拷贝构造函数给屏蔽了,因此在拷贝构造s3时会调用编译器默认的拷贝构造函数,那么这就会导致一个问题:
s2与s3共用同一块内存空间,在释放时同一块内存空间被释放多次而会引起程序崩溃,这种方式被称作浅拷贝.
3.1.1:浅拷贝
浅拷贝:又被称作位拷贝,编译器直接是将另外一个对象的值拷贝复制过来.如果对象中管理资源,那么最后就会导致多个对象共享一份资源,当一个对象销毁时就会将资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还是有效的,那么因此当继续对资源进行操作时,就会发生访问违规~
举一个简单例子
就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我抢,玩具损坏.


那么该如何解决浅拷贝的问题呢,用深拷贝就可以即每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有存在任何矛盾.
3.1.2:深拷贝
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出,一般情况都是按照深拷贝方式提供.
4:总代码
4.1:string.h
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;//避免与库中的string冲突,因此使用命名空间
namespace MyString
{class string{typedef char* iterator;typedef const char* const_iterator;public://string()// :_str(new char[1])// , _size(0)// ,_capacity(0)//{// //空字符串只有\0,// _str[0] = '\0';//}构造非空字符串//string(const char * str)// //开辟空间,多开辟个空间存放\0// :_str(new char[strlen(str) + 1])//{// //开辟空间// _capacity = _size = strlen(str);// //拷贝数据// strcpy(_str, str);//}//给缺省值,构造空字符串时使用缺省值string(const char* str = "")//开辟空间,多开辟个空间存放\0:_str(new char[strlen(str) + 1]){//开辟空间_capacity = _size = strlen(str);//拷贝数据strcpy(_str, str);}/*拷贝构造函数s2(s1),使用s1拷贝构造s2*/string(string& s){//开辟空间char* temp = new char[s._capacity + 1];_str = temp;_capacity = s._capacity;_size = s._size;//拷贝数据strcpy(_str, s._str);}//s1 = s2 this---->s1 s2----->s//赋值运算符重载string& operator =(string& s){//开辟新空间char* temp = new char[s._capacity + 1];//释放旧空间delete _str;//拷贝数据strcpy(temp, s._str);//指向新空间_str = temp;_size = s._size;_capacity = s._capacity;return *this;}//T &,返回引用同样可以提高效率,有返回值的目的是为了支持连续赋值.char& operator[](size_t position){assert(position < _size);return _str[position];}//const成员函数,给const对象调用,与上面构成函数重载const char& operator[](size_t position) const{assert(position < _size);return _str[position];}/////iteraoriterator begin(){return _str;}iterator end(){return _str + _size;}//设置成员函数const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}//capacitysize_t size(){return _size;}size_t capacity(){return _capacity;}//仅需判断是否比capacity大void reserve(size_t n){if (n > _capacity){//开辟新空间char* temp = new char[n + 1];//拷贝数据strcpy(temp, _str);//释放旧空间delete[] _str;//指向新空间_str = temp;_capacity = n;}}void resize(size_t n, char ch = '\0'){//比size小则会进行删除if (n <= _size){_str[n] = ch;}//比size大比capacity小会改变size,并且用\0插入else if (n > _size && n <= _capacity){_size = n;}//比capacity大, 会先改变size的大小同时进行扩容.else{//扩容reserve(n);for (size_t i = _size; i < _capacity; i++){_str[i] = ch;}_size = n;}}//Modifiersvoid push_back(char ch){//检查容量if (_size == _capacity)//二倍扩容reserve(_capacity == 0 ? 4 : 2 * _capacity);_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){assert(str);//求出添加的字符串的长度int length = strlen(str);//判断添加后的有效字符数量是否越过容量if (_size + length > _capacity)reserve(_size + length);//_str + _size的位置为\0strcpy(_str + _size, str);_size += length;}string& insert(size_t position, char ch){assert(position <= _size);if (_size == _capacity)//二倍扩容reserve(_capacity == 0 ? 4 : 2 * _capacity);//从后向前挪动覆盖for (int i = _size - 1; i >= position; i--){_str[i + 1] = _str[i];}_str[position] = ch;_size++;_str[_size] = '\0';return *this;}void clear(){_str[0] = '\0';_size = 0;}bool empty()const{return _size == 0;}string& insert(size_t position, const char* str){assert(position <= _size);int length = strlen(str);if (_size + length > _capacity)reserve(_size + length);//从后向前挪动覆盖//_size + length - 1表示挪动的字符串的重点,positon + length表示挪动的字符串的起点for (int i = _size + length - 1; i >= position + length; i--){_str[i] = _str[i - length];}//拷贝数据strncpy(_str + position, str, length);_size += length;_str[_size] = '\0';return *this;}void erase(size_t position = 0, size_t len = -1){//防止越界assert(position < _size);//len == -1:如果 len 为 -1,表示删除到字符串末尾。//len >= _size - pos,如果 len 超过从 pos 开始的剩余字符长度,同样认为是删除到末尾。if (len == -1 || len >= _size - position){_str[position] = '\0';_size = position;}else{//将 pos + len 后的字符串内容复制到 pos 位置,从而覆盖中间 len 长度的字符,实现删除操作strcpy(_str + position, _str + position + len);}}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char * str){append(str);return *this;}const char * c_str()const{return _str;}//添加const修饰,令const成员与非const成员均可调用size_t find(char ch, size_t pos = 0) const{for(size_t i = pos; i < _size;i++){if (_str[i] == ch)return i;}return -1;}//添加const修饰,令const成员与非const成员均可调用size_t find(const char * str,size_t pos = 0 )const{const char* p = strstr(_str + pos, str);if(p != NULL){//指针 - 指针得到对应的首字符下标return p - _str;}return -1;}string substr(size_t pos = 0, size_t len = -1){string substr;//len == -1:如果 len 为 -1,表示全部获取。//len > _size - pos,如果 len 超过从 pos 开始的剩余字符长度,同样认为是全部获取if(len > _size - pos || len == -1){for(size_t i = 0; i < _size; i++){substr += _str[i];}}else{for(size_t i = pos; i < pos + len;i++){substr += _str[i];}}return substr;}//s1.swap(s2)void swap(string & str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}//析构函数~string(){delete[] _str;_size = _capacity = 0;}private://定义成员变量并且给缺省值char* _str = nullptr;int _size = 0;int _capacity = 0;};//Non-member_function_overloadsostream& operator<<(ostream& _cout,MyString::string& str){for (auto ch : str){cout << ch;}return _cout;}istream& operator>>(istream& _cin, MyString::string& str){第一版:会导致空间的浪费//char ch;_cin是无法读到\n与' '的//ch = _cin.get();清空字符//str.clear();//while (ch != '\n' && ch != ' ')//{// str += ch;// ch = _cin.get();//}//return _cin;//第二版char ch;ch = _cin.get();//底层给个buffchar buff[128];size_t i = 0;//将原本的字符情况str.clear();//cin无法读取' '与'\n'while (ch != '\n' && ch != ' '){buff[i++] = ch;//最后一个字符设置为\0if(i == 127){buff[i] = '\0';str += buff;}//持续读取字符ch = _cin.get();}if(i > 0){buff[i] = '\0';str += buff;}return _cin;}//会先调用此swap,有现成的,吃现成的,不使用模版里面的swapvoid swap(string& x, string& y){x.swap(y);}//自定义类型传值传参会调用拷贝构造,因此需要传引用bool operator==(const MyString::string& str1, const MyString::string& str2){int result = strcmp(str1.c_str(), str2.c_str());return result == 0;}bool operator<(const MyString::string& str1, const MyString::string& str2){int result = strcmp(str1.c_str(), str2.c_str());return result < 0;}bool operator>(const MyString::string& str1, const MyString::string& str2){int result = strcmp(str1.c_str(), str2.c_str());return result > 0;}bool operator<=(const MyString::string& str1, const MyString::string& str2){return (str1 < str2) || (str1 == str2);}bool operator>=(const MyString::string& str1, const MyString::string& str2){return !(str1 < str2);}bool operator!=(const MyString::string& str1, const MyString::string& str2){return !(str1 == str2);}
}
4.2:Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "String.h"void TestConstructionAndCopyConstruction()
{MyString::string s1;MyString::string s2("hello world");MyString::string s3(s2);
}void TestAssignmentOperatorOverloadingAndElementAccess()
{MyString::string s1;MyString::string s2("hello world");const MyString::string s3("hello Linux");s1 = s2;cout <<"s1[0]:>" << s1[0] << endl;cout <<"s3[6]:>" << s3[6] << endl;
}void TestIteratorAndCapacity()
{MyString::string s1;MyString::string s2("hello world");s1 = s2;for(auto & element : s2){cout << element;}cout << endl;cout <<"s2.size():>" << s2.size() << endl;cout <<"s2.capacity():>" << s2.capacity() << endl;
}void TestResize()
{MyString::string str("hello world");//比size小则进行删除str.resize(10);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << endl;//比size大但小于capacity会用改变size,并用\0插入str.resize(13);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << endl;//比capacity大,会先改变size的大小同时进行扩容str.resize(20);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;
}void TestRserve()
{MyString::string str("hello world");cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << "reserver后" << endl;cout << endl;//比size小不变化str.reserve(10);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << endl;//比size大但小于capacity也不发生变化str.reserve(13);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;cout << endl;//比capacity大才会进行扩容str.reserve(20);cout << "size:>" << str.size() << endl;cout << "capacity:>" << str.capacity() << endl;
}void TestPushBackAndAppend()
{MyString::string str;str.append("hello world");str.push_back('x');
}
void TestInsert()
{MyString::string str("hello world");//在下标为5的位置插入字符hstr.insert(5, 'h');str.insert(2, "Linux");
}void TestErase()
{MyString::string str("hello world");str.erase(2, 4);
}void Test()
{MyString::string str("hello");str += ' ';str += "Linux";
}void TestFind()
{MyString::string str("hello Linux");cout << str.find('l', 2) << endl;cout << str.find("Linux", 2) << endl;
}
void TestSwapAndSubstr()
{MyString::string s1("hello Linux");MyString::string s2("hello bit");MyString::string temp = s1.substr(6, 5);cout << "交换前" << endl;cout << "s1:>" << s1 << endl;cout << "s2:>" << s2 << endl;cout <<"temp:>" << temp << endl;s1.swap(s2);cout << "交换后" << endl;cout <<"s1:>" << s1 << endl;cout <<"s2:>" << s2 << endl;
}void TestStreamInsertionAndStreamExtraction()
{MyString::string s1;cin >> s1;cout << s1 << endl;cout << "s1.size:>" << s1.size() << endl;cout << "s1.capacity:>" << s1.capacity() << endl;
}void TestOtherFunction()
{MyString::string s1("hello world");MyString::string s2("hello bit");swap(s1, s2);s1.swap(s2);cout << s1.c_str() << endl;cout << s2.c_str() << endl;cout << (s1 == s2) << endl;cout << (s1 >= s2) << endl;cout << (s1 > s2) << endl;cout << (s1 < s2) << endl;cout << (s1 <= s2) << endl;cout << (s1 != s2) << endl;
}int main()
{TestConstructionAndCopyConstruction();TestAssignmentOperatorOverloadingAndElementAccess();TestIteratorAndCapacity();TestResize();TestRserve();TestPushBackAndAppend();TestInsert();TestErase();Test();TestFind();TestSwapAndSubstr();TestStreamInsertionAndStreamExtraction();TestOtherFunction();return 0;
}
好啦,uu们,string的模拟实现这部分滴详细内容博主就讲到这里啦,如果uu们觉得博主讲的不错的话,请动动你们滴小手给博主点点赞,你们滴鼓励将成为博主源源不断滴动力,同时也欢迎大家来指正博主滴错误~
相关文章:

深入剖析String类的底层实现原理
嘿嘿,家人们,今天咱们来模拟实现string,好啦,废话不多讲,开干! 1:string.h 1.1:构造函数与拷贝构造函数 1.1.1:写法一 1.1.2:写法二(给缺省值) 1.2:赋值运算符重载与operatror[]获取元素 1.3:容量与迭代器 1.4:reserve与resize 1.5:清空与判断是否为空 1.6:push_back与…...
#其它:面试题
第一面试官提问如下: 1、自我介绍 2、根据项目提问:混合开发调取api的通讯方式 3、技术提问:如何隐藏div,但是div需要存在 使用 visibility 隐藏: 1.visibility: hidden2.display: none 3.opcity: 04、css塌陷问题…...

计算机视觉中的双边滤波:经典案例与Python代码解析
🌟 计算机视觉中的双边滤波:经典案例与Python代码解析 🚀 Hey小伙伴们!今天我们要聊的是计算机视觉中的一个重要技术——双边滤波。双边滤波是一种非线性滤波方法,主要用于图像去噪和平滑,同时保留图像的边…...
【AI日记】24.11.17 看 GraphRAG 论文,了解月之暗面
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 核心工作 内容:看 GraphRAG 论文时间:4 小时评估:不错,继续 非核心工作 内容:了解国内大模型方向,重点了解了创业独角兽-月之暗面&…...

Front Panel Window Bounds 与 Front Panel Window Bounds 的区别与应用
在LabVIEW中,Front Panel Window Bounds 和 Front Panel WindowBounds 是两个不同的属性节点,用于描述前面板窗口的位置和大小。它们的区别主要体现在它们表示的是窗口的不同部分,具体如下: 1 Window Bounds:调整整个…...
比较TCP/IP和OSI/RM的区别
一、结构不同 1、OSI:OSI划分为7层结构:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。 2、TCP/IP:TCP/IP划分为4层结构:应用层、传输层、互联网络层和主机-网络层。 二、性质不同 1、OSI:OSI是制定…...

【Java项目】基于SpringBoot的【招聘信息管理系统】
技术简介:系统软件架构选择B/S模式、SpringBoot框架、java技术和MySQL数据库等,总体功能模块运用自顶向下的分层思想。 系统简介:招聘信息管理系统的功能分为管理员,用户和企业三个部分,系统的主要功能包括首页、个人中…...

【论文笔记】LLaMA-VID: An Image is Worth 2 Tokens in Large Language Models
🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。 基本信息 标题: LLaMA-VID: An Image is W…...

使用Web Storage API实现客户端数据持久化
💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 使用Web Storage API实现客户端数据持久化 使用Web Storage API实现客户端数据持久化 使用Web Storage API实现客户端数据持久化…...
基于STM32F103的秒表设计-液晶显示
基于STM32F103的秒表设计-液晶显示 仿真软件: Proteus 8.17 编程软件: Keil 5 仿真实现: 在液晶1602上进行秒表显示,每100ms改变一次数值,一共三个按键,分为启动按键、暂停按键、复位按键。 电路介绍: 前面章节里已经和大家介绍了使用数码管设计的秒表,本次仿真将数…...
ReentrantLock的具体实现细节是什么
在 JDK 1.5 之前共享对象的协调机制只有 synchronized 和 volatile,在 JDK 1.5 中增加了新的机制 ReentrantLock,该机制的诞生并不是为了替代 synchronized,而是在 synchronized 不适用的情况下,提供一种可以选择的高级功能。 在 Java 中每个对象都隐式包含一个 monitor(监…...
【JavaScript】this 指向
1、this 指向谁 多数情况下,this 指向调用它所在方法的那个对象。即谁调的函数,this 就归谁。 当调用方法没有明确对象时,this 就指向全局对象。在浏览器中,指向 window;在 Node 中,指向 Global。&#x…...

DB Type
P位 p 1时段描述符有效,p 0时段描述符无效 Base Base被分成了三个部分,按照实际拼接即可 G位 如果G 0 说明描述符中Limit的单位是字节,如果是G 1 ,那么limit的描述的单位是页也就是4kb S位 S 1 表示代码段或者数据段描…...
python-返回函数
Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数! 例如,定义一个函数 f(),我们让它返回一个函数 g,可以这样写: def f()ÿ…...
python语言基础-5 进阶语法-5.2 装饰器-5.2.1 闭包
声明:本内容非盈利性质,也不支持任何组织或个人将其用作盈利用途。本内容来源于参考书或网站,会尽量附上原文链接,并鼓励大家看原文。侵删。 5.2 装饰器 python中的装饰器相当于java中的注解。装饰器用于为函数添加某些修饰性、…...

用vscode编写verilog时,如何有信号定义提示、信号定义跳转(go to definition)、模块跳转(跨文件跳转)这些功能
(一)方法一:安装插件SystemVerilog - Language Support 安装一个vscode插件即可,插件叫SystemVerilog - Language Support。虽然说另一个插件“Verilog-HDL/SystemVerilog/Bluespec SystemVerilog”也有信号提示及定义跳转功能&am…...
MQTT+Springboot整合
1.mqttconfig配置(配置参数是从数据库查出来的) package com.terminal.dc3.api.center.manager.config;import com.collection.common.utils.StringUtils; import com.collection.system.mapper.MqttConfigMapper; import lombok.Data; import org.springframework.beans.fact…...

ERROR TypeError: AutoImport is not a function
TypeError: AutoImport is not a function 原因:unplugin-auto-import 插件版本问题 Vue3基于Webpack,在vue.config.js中配置 当unplugin-vue-components版本小于0.26.0时,使用以下写法 const { defineConfig } require("vue/cli-se…...

软考教材重点内容 信息安全工程师 第 3 章 密码学基本理论
(本章相对老版本极大的简化,所有与算法相关的计算全部删除,因此考试需要了解各个常 用算法的基本参数以及考试中可能存在的古典密码算法的计算,典型的例子是 2021 和 2022 年分别考了 DES 算法中的 S 盒计算,RSA 中的已…...

微信小程序 https://thirdwx.qlogo.cn 不在以下 downloadFile 合法域名列表中
授权登录后,拿到用户头像进行加载,但报错提示: https://thirdwx.qlogo.cn 不在以下 downloadFile 合法域名列表中 解决方法一(未完全解决,临时处理):在微信开发者工具将不校验...勾上就可以访问…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建
【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...

GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...