嘿嘿,家人们,今天咱们来模拟实现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们觉得博主讲的不错的话,请动动你们滴小手给博主点点赞,你们滴鼓励将成为博主源源不断滴动力,同时也欢迎大家来指正博主滴错误~