当前位置: 首页 > news >正文

深入剖析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与…...

#其它:面试题

第一面试官提问如下&#xff1a; 1、自我介绍 2、根据项目提问&#xff1a;混合开发调取api的通讯方式 3、技术提问&#xff1a;如何隐藏div&#xff0c;但是div需要存在 使用 visibility 隐藏&#xff1a; 1.visibility: hidden2.display: none 3.opcity: 04、css塌陷问题…...

计算机视觉中的双边滤波:经典案例与Python代码解析

&#x1f31f; 计算机视觉中的双边滤波&#xff1a;经典案例与Python代码解析 &#x1f680; Hey小伙伴们&#xff01;今天我们要聊的是计算机视觉中的一个重要技术——双边滤波。双边滤波是一种非线性滤波方法&#xff0c;主要用于图像去噪和平滑&#xff0c;同时保留图像的边…...

【AI日记】24.11.17 看 GraphRAG 论文,了解月之暗面

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 核心工作 内容&#xff1a;看 GraphRAG 论文时间&#xff1a;4 小时评估&#xff1a;不错&#xff0c;继续 非核心工作 内容&#xff1a;了解国内大模型方向&#xff0c;重点了解了创业独角兽-月之暗面&…...

Front Panel Window Bounds 与 Front Panel Window Bounds 的区别与应用

在LabVIEW中&#xff0c;Front Panel Window Bounds 和 Front Panel WindowBounds 是两个不同的属性节点&#xff0c;用于描述前面板窗口的位置和大小。它们的区别主要体现在它们表示的是窗口的不同部分&#xff0c;具体如下&#xff1a; 1 Window Bounds&#xff1a;调整整个…...

比较TCP/IP和OSI/RM的区别

一、结构不同 1、OSI&#xff1a;OSI划分为7层结构&#xff1a;物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。 2、TCP/IP&#xff1a;TCP/IP划分为4层结构&#xff1a;应用层、传输层、互联网络层和主机-网络层。 二、性质不同 1、OSI&#xff1a;OSI是制定…...

【Java项目】基于SpringBoot的【招聘信息管理系统】

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

【论文笔记】LLaMA-VID: An Image is Worth 2 Tokens in Large Language Models

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: LLaMA-VID: An Image is W…...

使用Web Storage API实现客户端数据持久化

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用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 指向谁 多数情况下&#xff0c;this 指向调用它所在方法的那个对象。即谁调的函数&#xff0c;this 就归谁。 当调用方法没有明确对象时&#xff0c;this 就指向全局对象。在浏览器中&#xff0c;指向 window&#xff1b;在 Node 中&#xff0c;指向 Global。&#x…...

DB Type

P位 p 1时段描述符有效&#xff0c;p 0时段描述符无效 Base Base被分成了三个部分&#xff0c;按照实际拼接即可 G位 如果G 0 说明描述符中Limit的单位是字节&#xff0c;如果是G 1 &#xff0c;那么limit的描述的单位是页也就是4kb S位 S 1 表示代码段或者数据段描…...

python-返回函数

Python的函数不但可以返回int、str、list、dict等数据类型&#xff0c;还可以返回函数&#xff01; 例如&#xff0c;定义一个函数 f&#xff08;&#xff09;&#xff0c;我们让它返回一个函数 g&#xff0c;可以这样写&#xff1a; def f&#xff08;&#xff09;&#xff…...

python语言基础-5 进阶语法-5.2 装饰器-5.2.1 闭包

声明&#xff1a;本内容非盈利性质&#xff0c;也不支持任何组织或个人将其用作盈利用途。本内容来源于参考书或网站&#xff0c;会尽量附上原文链接&#xff0c;并鼓励大家看原文。侵删。 5.2 装饰器 python中的装饰器相当于java中的注解。装饰器用于为函数添加某些修饰性、…...

用vscode编写verilog时,如何有信号定义提示、信号定义跳转(go to definition)、模块跳转(跨文件跳转)这些功能

&#xff08;一&#xff09;方法一&#xff1a;安装插件SystemVerilog - Language Support 安装一个vscode插件即可&#xff0c;插件叫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 原因&#xff1a;unplugin-auto-import 插件版本问题 Vue3基于Webpack&#xff0c;在vue.config.js中配置 当unplugin-vue-components版本小于0.26.0时&#xff0c;使用以下写法 const { defineConfig } require("vue/cli-se…...

软考教材重点内容 信息安全工程师 第 3 章 密码学基本理论

&#xff08;本章相对老版本极大的简化&#xff0c;所有与算法相关的计算全部删除&#xff0c;因此考试需要了解各个常 用算法的基本参数以及考试中可能存在的古典密码算法的计算&#xff0c;典型的例子是 2021 和 2022 年分别考了 DES 算法中的 S 盒计算&#xff0c;RSA 中的已…...

微信小程序 https://thirdwx.qlogo.cn 不在以下 downloadFile 合法域名列表中

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

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...