深入剖析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 合法域名列表中 解决方法一(未完全解决,临时处理):在微信开发者工具将不校验...勾上就可以访问…...

Linux性能优化之火焰图的起源
Linux火焰图的起源与性能优化专家 Brendan Gregg 密切相关,他在 2011 年首次提出这一工具,用于解决性能分析过程中可视化和数据解读的难题。 1. 背景:性能优化的需求 在现代计算中,性能优化往往需要对程序执行中的热点和瓶颈进行…...
《Markdown语法入门》
文章目录 《Markdown语法入门》1.标题2.段落2.1 换行2.2分割线 3.文字显示3.1 字体3.2 上下标 4. 列表4.1无序列表4.2 有序列表4.3 任务列表 5. 区块显示6. 代码显示6.1 行内代码6.2 代码块 7.插入超链接8.插入图片9. 插入表格 《Markdown语法入门》 【Typora 教程】手把手教你…...

Controller Baseband commands速览
目录 一、设备连接与通信控制类(34条) 1.1. 连接参数相关 1.1.1. 连接建立超时设置 1.1.2. 链路监督超时设置 1.1.3. Page操作超时设置 1.1.4. 扩展Page操作超时设置 1.1.5. 安全连接主机支持 1.2. 扫描操作相关 1.2.1. 扫描启用与禁用 1.2.2.…...

Redisson 3.39.0 发布
Redisson 3.39.0 发布,官方推荐的 Redis 客户端 Redisson 3.38.0 ,一个 Java 编写的 Redis 客户端。 此版本更新内容如下: RTopic 对象的 partitioning 实现 RShardedTopic对象的 partitioning 实现 RReliableTopic 对象的 partitioning 实…...

高阶C语言补充:柔性数组
C99中,结构体中最后一个元素允许时未知大小的数组,这就叫做柔性数组成员。 vs编译器也支持柔性数组。 之所以把柔性数组单独列出,是因为: 1、柔性数组是建立在结构体的基础上的。 2、柔性数组的使用用到了动态内存分配。 这使得柔…...

S32K324信息安全-使用IC5000/IC5700进行debug口解锁
文章目录 前言winIDEA配置参考 前言 由于信息安全要求,需要对debug口(JTAG)进行加密,本文介绍基于固定密码的方式,使用IC5000/IC5700进行debug口解锁的方法 winIDEA配置 点击 Hardware | CPU Options | Reset | Ini…...

简单实现QT对象的[json]序列化与反序列化
简单实现QT对象的[json]序列化与反序列化 简介应用场景qt元对象系统思路实现使用方式题外话 简介 众所周知json作为一种轻量级的数据交换格式,在开发中被广泛应用。因此如何方便的将对象数据转为json格式和从json格式中加载数据到对象中就变得尤为重要。 在python类…...

Unity肢体控制(关节控制)
前面的基础搭建网上自己搜,我这个任务模型网上也有,可以去官网看看更多模型,这里只讲述有模型如何驱动肢体的操作方式 第一步:创建脚本 第二步:创建Rig Builder 建空容器 加部件(Rig),加了之后…...

Node.js | Yarn下载安装与环境配置
一、安装Node.js Yarn 是 Node.js 下的包管理工具,因此想要使用 Yarn 就必须先下载 Node.js。 推荐参考:Node.js | npm下载安装及环境配置教程 二、Yarn安装 打开cmd,输入以下命令: npm install -g yarn检查是否安装成功&…...

WPF如何全局应用黑白主题效果
灰白色很多时候用于纪念,哀悼等。那么使用 WPF如何来做到这种效果呢?要实现的这种效果,我们会发现,它其实不仅仅是要针对图片,而是要针对整个窗口来实现灰白色。 如果只是针对图片的话,我可以可以对图片进…...