上期我们详细讲解了string,本期为了更深入理解string,我们自己手动实现一下string类。
话不多说,下面是模拟string的源码以及测试代码(关于代码的详细讲解都标在注释中了哦):
string.h (模拟实现string代码):
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <cassert>
#include <ostream>
namespace lhs
{
class string
{
public:
//模拟实现迭代器
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;//返回字符串最后一个元素后一个位置的地址
}
//构造函数
string(const char* str = "")//这里缺省值给空串,默认为"\0"。使用const来修饰是为了方便解决传入常量字符串类型不匹配的问题
:_size(strlen(str))
{
_capacity = _size;
_str = new char[_capacity + 1];//_capaicty + 1是为了统计字符串最后的"\0"
strcpy(_str, str);//拷贝传入的字符串至string类的动态开辟的空间中
}
//拷贝构造函数
string(const string& s)
:_size(s._size)
,_capacity(s._capacity)
{
_str = new char[_capacity + 1];//_capaicty + 1是为了预留字符串最后的"\0"位置
strcpy(_str, s._str);//拷贝传入string类的字符串至新的string类的动态开辟的空间中
}
//size函数接口
size_t size()const
{
return _size;
}
//capacity函数接口
size_t capacity()const
{
return _capacity;
}
//c_str函数接口
const char* c_str()const
{
return _str;
}
//reserve接口
void reserve(size_t n)
{
if (n > _capacity)//保证空间不会缩容
{
char* temp = new char[n + 1];//n + 1是为了预留字符串最后的"\0"位置,这里使用临时变量来接收,防止开辟空间失败
strcpy(temp, _str);//拷贝数据
delete[] _str;//释放旧空间
_str = temp;//重新指向新空间
_capacity = n;
}
}
//resize函数接口
void resize(size_t n, const char ch = '\0')
{
if (n <= _size)//删除多余数据
{
_str[n] = '\0';
_size = n;
}
else//size外剩余数据初始化
{
if (n > _capacity)//判断是否需要扩容
{
reserve(n);
}
//初始化数据
for (size_t i = _size; i < _capacity; ++i)
{
_str[i] = ch;
}
_str[_capacity] = '\0';
_size = n;
}
}
//push_back接口
void push_back(const char ch)
{
if (_size + 1 > _capacity)//判断字符串容量是否足够
{
if (_capacity == 0)//对于字符串容量为0的情况特殊对待
{
reserve(2);//扩容
}
else
{
reserve(_size * 2);//扩容
}
}
_str[_size] = ch;//追加字符
++_size;
_str[_size] = '\0';//追加完字符后别忘了添上'\0'哦
}
//append接口
void append(const char* str)
{
size_t n = strlen(str);
if (_size + n > _capacity)//判断字符串容量是否足够
{
reserve(_size + n);//扩容
}
strcpy(_str + _size, str);//追加字符串
_size += n;
}
//insert函数接口
string& insert(size_t pos, const char ch)
{
assert(pos <= _size);
if (_size + 1 > _capacity)//判断字符串容量是否足够
{
reserve(2 * _capacity);
}
//移动数据
size_t begin = _size;
while (begin > pos)
{
_str[begin] = _str[begin - 1];
--begin;
}
//插入字符
_str[pos] = ch;
++_size;
_str[_size] = '\0';//插入完字符后别忘了添上'\0'哦
return *this;
}
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size + len > _capacity)//判断字符串容量是否足够
{
reserve(_size + len);
}
//移动数据,在这个移动过程中字符串的'\0'一起移动了
size_t begin = _size + len;
while (begin >= pos + len)
{
_str[begin] = _str[begin - len];
--begin;
}
//插入字符串
for (size_t i = pos, n = 0; i < pos + len; ++i, ++n)
{
_str[i] = str[n];
}
_size += len;
return *this;
}
//erase函数接口
string& erase(size_t pos, size_t len = npos)
{
assert(pos <= _size);
if (len == npos || len + pos > _size)//删除pos后所有数据
{
_str[pos] = '\0';
_size = pos;
}
else//删除pos后len个数据
{
//移动数据
for (size_t i = pos; i <= _size - len; ++i)
{
_str[i] = _str[i + len];
}
_size -= len;
}
return *this;
}
//swap函数接口
void swap(string& s)
{
//临时变量储存数据
size_t Tsize = s._size, Tcapacity = s._capacity;
char* Tstr = s._str;
//交换数据
s._size= _size;
s._capacity= _capacity;
s._str= _str;
_size = Tsize;
_capacity = Tcapacity;
_str = Tstr;
}
//find函数接口
size_t find(const char ch, size_t pos = 0)
{
assert(pos <= _size);
//查找字符
for (size_t i = pos; i < _size; ++i)
{
if (_str[i] == ch)
return i;
}
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
assert(pos <= _size);
//暴力查找字符串
size_t len = strlen(str);
for (size_t i = pos; i < _size; ++i)
{
size_t j = 0;
for (; j < len; ++j)
{
if (i + len > _size)
break;
if (_str[i + j] != str[j])
break;
}
if (j == len)
return i;
}
return npos;
}
//clean函数接口
void clean()
{
_str[0] = '\0';
_size = 0;
}
//运算符重载
char& operator[](size_t pos)//可修改引用
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos)const//不可修改常量引用
{
assert(pos < _size);
return _str[pos];
}
string& operator=(const string& s)
{
if (this != &s)//防止自身对自身进行赋值
{
_size = s._size;
_capacity = s._capacity;
char* temp = new char[_capacity + 1];//这里使用临时变量来接收,防止开辟空间失败
delete[] _str;//释放旧空间
_str = temp;//重新指向新空间
strcpy(_str, s._str);
}
return *this;
}
bool operator>(const string& s)const
{
size_t min = s._size > _size ? _size : s._size;
//一个个按字符的ASCLL码进行比较
size_t i = 0;
for (; i < min; ++i)
{
if (_str[i] > s._str[i])
return true;
}
if (i == s._size && s._size != _size)
return true;
else
return false;
}
bool operator==(const string& s)const
{
if (s._size != _size)
return false;
//一个个按字符的ASCLL码进行比较
for (size_t i = 0; i < _size; ++i)
{
if (_str[i] != s._str[i])
return false;
}
return true;
}
bool operator<(const string& s)const
{
return !(*this > s || *this == s);
}
bool operator>=(const string& s)const
{
return *this > s || *this == s;
}
bool operator<=(const string& s)const
{
return *this < s || *this == s;
}
bool operator!=(const string& s)const
{
return !(*this == s);
}
string& operator+=(const char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
//析构函数
~string()
{
delete[] _str;
}
private:
size_t _size;//有效字符数
size_t _capacity;//容量大小
char* _str;//有效数据
static const size_t npos;
};
const size_t string::npos = -1;
//流插入
std::ostream & operator<<(std::ostream & out,const string& s)
{
for (auto ch: s)
{
std::cout << ch;
}
std::cout << std::endl;
return out;
}
//流提取
std::istream& operator>>(std::istream& in, string& s)
{
s.clean();
char ch = in.get();//为了拿到键盘输入的所有字符这里用到了in.get函数,普通的流提取>>会将空格和换行字符流入到缓冲区中
char buff[188];//为了防止输入的字符串过长,导致string的频繁扩容,这里使用一个字符数组来缓冲,将数组填满后再存入string的字符串中
size_t i = 0;
while (ch!='\n')//一直一个一个拿键盘中输入的字符,直到遇到换行符
{
if (i == 187)//数组存满后,将数组中的数据插入到string中,防止频繁扩容
{
buff[187] = '\0';
s += buff;
i = 0;
}
buff[i++] = ch;//将拿到的字符插入到数组中
ch = in.get();//继续拿下一个字符
}
if (i != 0)//将数组未存入string的数据存入string
{
buff[i] = '\0';
s += buff;
}
return in;
}
}
StringTest.cpp (测试代码):
#include"string.h"
void Test1()
{
lhs::string s1;
lhs::string s2("Hello world");
std::cout << s1.c_str() << std::endl;
std::cout << s2.c_str() << std::endl;
++s2[8];
std::cout << s2.c_str() << std::endl;
}
void Test2()
{
lhs::string s1="Hello world";
lhs::string s2(s1);
std::cout << s1.c_str() << std::endl;
std::cout << s2.c_str() << std::endl;
++s1[4];
++s2[9];
std::cout << s1.c_str() << std::endl;
std::cout << s2.c_str() << std::endl;
}
void Test3()
{
lhs::string s1 = "Hello world";
lhs::string s2 = s1;
std::cout << s1.c_str() << std::endl;
std::cout << s2.c_str() << std::endl;
++s1[4];
++s2[9];
std::cout << s1.c_str() << std::endl;
std::cout << s2.c_str() << std::endl;
s2 = s2;
std::cout << s2.c_str() << std::endl;
}
void Print(const lhs::string& s)
{
for (size_t i = 0; i < s.size(); ++i)
{
std::cout << s[i];
}
std::cout << std::endl;
for (auto ch : s)//自己实现了迭代器,范围for就可以自动识别了
{
std::cout << ch;
}
std::cout << std::endl;
}
void Test4()
{
lhs::string s1 = "Hello world";
std::cout << s1.c_str() << std::endl;
Print(s1);
}
void Test5()
{
lhs::string s1 = "Hello world";
lhs::string::iterator it = s1.begin();
while (it != s1.end())
{
std::cout << *it;
++it;
}
std::cout << std::endl;
it = s1.begin();
while (it != s1.end())
{
--(*it);
std::cout << *it;
++it;
}
std::cout << std::endl;
for (auto ch : s1)//自己实现了迭代器,范围for就可以自动识别了
{
std::cout << ch;
}
std::cout << std::endl;
}
void Test6()
{
lhs::string s1 = "Hello world";
lhs::string s2 = "hello world";
std::cout << (s1 > s2) << std::endl;
std::cout << (s1 == s2) << std::endl;
std::cout << (s1 < s2) << std::endl;
std::cout << (s1 >= s2) << std::endl;
std::cout << (s1 <= s2) << std::endl;
std::cout << (s1 != s2) << std::endl;
}
void Test7()
{
lhs::string s1;
std::cout << s1.capacity() << std::endl;
s1.push_back('H');
std::cout << s1.capacity() << std::endl;
s1.push_back('e');
std::cout << s1.capacity() << std::endl;
s1.append("llo world");
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
}
void Test8()
{
lhs::string s1 = "Hello";
std::cout << s1.capacity() << std::endl;
s1 += ' ';
std::cout << s1.capacity() << std::endl;
s1 += 'w';
std::cout << s1.capacity() << std::endl;
s1 += "orld";
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
}
void Test9()
{
lhs::string s1 = "Hello world";
s1.resize(16,'h');
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
s1.resize(6);
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
s1.resize(20);
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
}
void Test10()
{
lhs::string s1 = "Hell world";
s1.insert(4, 'o');
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
s1.insert(0, ' ');
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
}
void Test11()
{
lhs::string s1 = "world";
s1.insert(0, "Hello ");
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
s1.insert(5, " 1111");
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
}
void Test12()
{
lhs::string s1 = "Hello444 world";
s1.erase(5, 3);
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
s1.erase(5);
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
}
void Test13()
{
lhs::string s1 = "Hello";
lhs::string s2 = "world !";
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
std::cout << s2.size() << std::endl;
std::cout << s2.capacity() << std::endl;
std::cout << s2.c_str() << std::endl;
s1.swap(s2);
std::cout << s1.size() << std::endl;
std::cout << s1.capacity() << std::endl;
std::cout << s1.c_str() << std::endl;
std::cout << s2.size() << std::endl;
std::cout << s2.capacity() << std::endl;
std::cout << s2.c_str() << std::endl;
}
void Test14()
{
lhs::string s1 = "Hello world";
std::cout << s1.find('o') << std::endl;
std::cout << s1.find('o', 6) << std::endl;
std::cout << s1.find("ello") << std::endl;
std::cout << s1.find("elo") << std::endl;
}
void Test15()
{
lhs::string s1 = "Hello";
std::cin >> s1;
std::cout << s1 << std::endl;
}
int main()
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
Test7();
Test8();
Test9();
Test10();
Test11();
Test12();
Test13();
Test14();
Test15();
return 0;
}
测试结果:
本期代码量巨大,如有bug或不足还请各位大佬在评论区指出!
下期见~