自学内容网 自学内容网

深入剖析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拷贝构造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;
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;
}

//capacity
size_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


//Modifiers
void 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的位置为\0
strcpy(_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();
//底层给个buff
char buff[128];
size_t i = 0;
//将原本的字符清空
str.clear();
//cin无法读取' '与'\n'
while (ch != '\n' && ch != ' ')
{
buff[i++] = ch;
//最后一个字符设置为\0
if(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,有现成的,吃现成的,不使用模版里面的swap
void 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的位置插入字符h
str.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的位置插入字符h
str.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];
}
///
//iteraor
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
//设置成员函数
const_iterator begin() const
{
return _str;
}

const_iterator end() const
{
return _str + _size;
}

//capacity
size_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;
}
}

//Modifiers
void 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的位置为\0
strcpy(_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_overloads
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();
//底层给个buff
char buff[128];
size_t i = 0;
//将原本的字符情况
str.clear();
//cin无法读取' '与'\n'
while (ch != '\n' && ch != ' ')
{
buff[i++] = ch;
//最后一个字符设置为\0
if(i == 127)
{
buff[i] = '\0';
str += buff;
}
//持续读取字符
ch = _cin.get();
}
if(i > 0)
{
buff[i] = '\0';
str += buff;
}
return _cin;
}

//会先调用此swap,有现成的,吃现成的,不使用模版里面的swap
void 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的位置插入字符h
str.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们觉得博主讲的不错的话,请动动你们滴小手给博主点点赞,你们滴鼓励将成为博主源源不断滴动力,同时也欢迎大家来指正博主滴错误~


原文地址:https://blog.csdn.net/ABWSO/article/details/143452342

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!