Hello!!大家早上中午晚上好,昨天复习了string的使用,今天来模拟实现一下string!!!
一、string的框架搭建
1.1首先我们需要一个string的头文件用来做变量、函数、类等声明;再需要一个test文件来做测试,还需要一个文件来实现方法;
包一下头文件ok初步工作完成;
1.2明确string需要用到的成员变量以及成员函数
成员变量:
①必不可少的一个指向字符串的char*指针;(注意这里不能设置为const char*)
②字符串存储的有效数据size_t size;(有效数据指的时'\0'之前的数据)
③字符串存储的那块空间的容量size_t capacity;
④一个无符号最大值npos(用来判断);
成员函数:
①string的构造函数;包括用字符串构造、用string对象构造、用n个字符构造、用迭代器区间构造;实现常用的几个即可;
②string的析构函数;(因为涉及深拷贝所以要写)
③string的运算符重载函数;包括赋值运算符重载,比较小于、大于、等于、不等于运算重载函数,还有下标访问的运算符重载函数,流插入流提取运算符重载函数;
普通成员函数:
①开空间并初始化的 resize函数;
②开空间不初始化的reserve函数;
③插入单个字符的push_back函数;
④插入字符串的+=运算符重载函数;
⑤某个位置插入n个字符的insert函数;
⑥某个位置删除n个字符的erase函数;
⑦获取string有效数据的size()函数;
⑧获取string存储空间大小的capacity()函数;
⑨清理字符串的clear函数;
⑩从某个位置开始查找某个字符的find函数;
11、从某个位置开始截取长度为len的字符串;
12、获取_str的c_str函数;
还有一个需要用到的是迭代器;
1.3声明到头文件
由于模拟实现的string类的名字会跟std里的string冲突,所以需要namespace一个命名空间;
#pragma once
#include <iostream>
using namespace std;
//string的模拟实现
namespace ldc
{
struct string
{
typedef char* iterator;
typedef const char* const_iterator;
public:
string(const char* str);
string(const string& s);
string(size_t n, char c);
string(const_iterator it1, const_iterator it2);
~string();
string& operator=(const string& s);
bool operator>(const string& s)const;
bool operator<(const string& s)const;
bool operator==(const string& s)const;
bool operator!=(const string& s)const;
bool operator >= (const string & s)const;
bool operator <=(const string& s)const;
char operator[](size_t pos);
const char operator[](size_t pos)const;
void resize(size_t n, char c='\0');
void reserve(size_t n);
void push_back(char c);
string& operator+=(const char* str);
string& opeartor += (const string & s);
string& ldc::string::operator+=(char ch);
void insert(size_t pos, size_t n, char c);
void erase(size_t pos, size_t n=npos);
size_t size();
size_t capacity();
void clear();
size_t find(char c, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);
string& substr(size_t pos = 0, size_t len = npos);
const char* c_str const();
private:
char* _str;
size_t _size;
size_t _capacity;
const static size_t npos=-1;
};
ostream& operator<<(ostream& out, const string& s);//流提出插入重载
istream& operator>>(istream& in, string& s);//在类外重载不存在this
}
这里注意:npos用来做判断,当调用erase在pos位置删除n个字符时,如果不传参n默认为npos(无符号最大值),表示从pos位置开始往后的全删除;(同理substr也一样);
二、开始实现
2.1构造函数的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include "string3_20.h"
#include <string.h>
//string的方法实现
//构造函数
ldc::string::string(const char* str="")
{
size_t len = strlen(str);
_size = len;
_capacity = _size;
_str = new char[_capacity + 1];//这里+1因为要保留一个位置存放'\0',空串也有'\0'
memcpy(_str, str, len + 1);
}
ldc::string::string(const string& s)
{
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
memcpy(_str, s._str, _capacity + 1);
}
ldc::string::string(size_t n, char c)
{
_size = n;
_capacity = _size;
_str = new char[_capacity + 1];
for (size_t i = 0; i < n; i++)
{
_str[i] = c;
}
_str[n] = '\0';
}
ldc::string::string(const_iterator it1, const_iterator it2)
{
_size = it2 - it1;
_capacity = _size;
_str = new char[_capacity + 1];
memcpy(_str, it1, _size);
_str[_size] = '\0';
}
顺便把流插入重载和c_str也实现了(好测试)
const char* ldc::string::c_str()const
{
return _str;
}
ostream& ldc::operator<<(ostream& out, const string& s)
{
out <<s.c_str();
return out;
}
测试:
#define _CRT_SECURE_NO_WARNINGS 1
//string的测试
#include "string3_20.h"
int main()
{
ldc::string s1("hello world!!"); //字符串构造
const char* ch = "welcome";
ldc::string s2(ch);
cout << s1 << endl;
cout << s2 << endl;
char ch2[10] = { "yes!!!" };
ldc::string s3(ch2, ch2 + 3);//区间构造
cout << s3 << endl;
ldc::string s4(s3);//拷贝构造
cout<<s4<<endl;
return 0;
}
ok没问题!接下来实现以下赋值重载跟析构;
2.2赋值重载与析构函数
ldc::string& ldc::string::operator=(const string& s)
{
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
memcpy(_str, s._str, _capacity + 1);
return *this;
}
ldc::string::~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
测试:
int main()
{
ldc::string s1("hello");
ldc::string s2("welcome");
s1 = s2;
cout << s1 << endl;
return 0;
}
没问题,接下来实现普通成员函数;
2.3普通成员函数的实现
//比较、下标访问运算符重载
bool ldc::string::operator>(const string& s)const
{
//比较有很多种方法,可以用operator[]比也可以获取str后再比,这里用最简洁的方法
//库里的memcmp内存比较函数
int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);//先比短
return ret == 0 ? _size > s._size:ret > 0;//再比长,如果相等ret为正数表示str1>str2
}
bool ldc::string::operator<=(const string& s)const
{
return !(*this > s);
}
bool ldc::string::operator<(const string& s)const
{
return !(*this >= s);
}
bool ldc::string::operator>=(const string& s)const
{
return (*this > s) || (*this == s);
}
bool ldc::string::operator==(const string& s)const
{
return _size == s._size && memcmp(_str, s._str, _size) == 0;
}
bool ldc::string::operator!=(const string& s)const
{
return !(*this == s);
}
char ldc::string::operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char ldc::string::operator[](size_t pos)const
{
assert(pos < _size);
return _str[pos];
}
测试:
void test1()
{
ldc::string s1("hello");
ldc::string s2("helloxxx");
ldc::string s3("welcome");
ldc::string s4("welcome");
cout << (s1 > s2) << " ";
cout << (s1 >= s2) << " ";
cout << (s1 < s2) << " ";
cout << (s1 <= s2) << " ";
cout << (s3 == s4) << " ";
cout << (s3!= s2) << " ";
cout << s1[2] << " ";
s1[0]='Y';
cout << s1 << " ";
//const ldc::string s5("beutiful!");
//s5[0] = 'B';
}
int main()
{
test1();
return 0;
}
OK没问题接着实现扩容:
2.4扩容实现
//只开空间不初始化
void ldc::string::reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//假设开100个空间,需要存放100个数据,第101个位置需存放结束标识符'\0'
memcpy(tmp, _str, _size + 1);//连'\0'一起拷贝,不然开完空间后的数据没有结束标志符
delete[] _str;//释放旧空间
_str = tmp;//指向新空间
_capacity = n;//注意只需要修改_capacity,_size有效数据始终没变
}
}
//开空间并初始化
void ldc::string::resize(size_t n, char c = '\0')
{
if (n < _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
reserve(n);
for (size_t i=_size;i<n;i++)
{
_str[i] = c;
}
_str[n] = '\0';
_size = n;
}
}
测试:
void test2()
{
ldc::string s1("hello");
s1.resize(20, 'x');
cout << s1 << endl;
s1.resize(3);
cout << s1 << endl;
}
int main()
{
//test1();
test2();
return 0;
}
ok没问题下一步,reserve后面测;
2.5增删查改实现
void ldc::string::push_back(char c)
{
if (_size == _capacity)
{
size_t n = _capacity == 0 ? 4 : _capacity * 2;//二倍扩容
reserve(n);
}
_str[_size] = c;
++_size;
_str[_size] = '\0';
}
ldc::string& ldc::string::operator+=(const char* str)
{
size_t len = strlen(str);
reserve(_size + len);//复用reserve开空间
memcpy(_str + _size, str, len + 1);//'\0'一起拷贝过去
_size += len;
return *this;
}
ldc::string& ldc::string::operator+=(char ch)
{
push_back(ch);
return *this;
}
ldc::string&ldc::string::operator+= (const string & s)
{
reserve(_size + s._size);
memcpy(_str + _size, s._str, s._size + 1);
_size += s._size;
return *this;
}
void ldc::string::insert(size_t pos, size_t n, char c)
{
assert(pos <= _size);
reserve(_size + n);
if (pos==_size)//尾插
{
while (n--)
{
push_back(c);
}
}
else
{
char* begin = _str + _size;
char* newbegin = _str + pos;
char* end = _str + _size + n;
while (begin>=newbegin)
{
*end = *begin;
--end;
--begin;
}
int i = n;
while (i--)
{
_str[pos] = c;
++pos;
}
_size += n;
}
}
void ldc::string::erase(size_t pos, size_t n)
{
assert(pos < _size);
if (n == npos || pos + n > _size)
{
_str[pos] = '\0';
}
else
{
size_t end = _size;
size_t begin = pos + n;
while (begin != end)
{
_str[pos] = _str[begin];
++pos; ++begin;
}
}
_size -= n;
_str[_size] = '\0';
}
size_t ldc::string::find(char c, size_t pos)
{
assert(pos < _size);
while (pos != _size)
{
if (_str[pos] == c)
{
return pos;
}
++pos;
}
return npos;
}
size_t ldc::string::find(const char* str, size_t pos)
{
//直接用strstr函数
char* ptr = strstr(_str + pos, str);
if (ptr)
{
return ptr - _str;
}
else
return npos;
}
测试:
void test3()
{
ldc::string s1("hello world!!");
s1.insert(5, 3, 'X');
cout << s1 << endl;
s1.insert(0, 3, 'Y');
cout << s1 << endl;
s1.insert(19, 2, 'Z');
cout << s1 << endl;
s1.erase(0, 3);
cout << s1 << endl;
s1.erase(5, 4);
cout << s1 << endl;
s1.erase(10, -1);
cout << s1 << endl;
cout << s1.find('h')<<endl;
cout << s1.find("world") << endl;
cout << s1.find('l', 3) << endl;
}
没啥问题,接着把剩下的都实现了;
三、剩余完善
3.1substr实现
ldc::string ldc::string::substr(size_t pos, size_t len)
{
assert(pos < _size);
size_t n = len;
if (n == npos || pos + n > _size)
{
n = _size - pos;
}
string temp;
temp.reserve(n);
for (size_t i = 0; i < n; i++)
{
temp += _str[pos + i];
}
return temp;
}
3.2其他实现
size_t ldc::string::size()const
{
return _size;
}
size_t ldc::string::capacity()const
{
return _capacity;
}
void ldc::string::clear()
{
_str[0] = '0';
_size = 0;
}
istream& ldc::operator>>(istream& in, string& s)
{
s.clear();
char ch = in.get();
// 处理前缓冲区前面的空格或者换行
while (ch == ' ' || ch == '\n')
{
ch = in.get();
}
//in >> ch;
char buff[128];
int i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
s += buff;
i = 0;
}
//in >> ch;
ch = in.get();
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
3.3测试
void test4()
{
ldc::string s1("hello");
ldc::string s2(s1.substr(0, 2));
cout << s1 << endl;
cout << s2 << endl;
cout <<"开空间前:" << s2.capacity() <<" ," << s2.size() << endl;
s2.reserve(40);
cout <<"开空间后:"<< s2.size() << ", " << s2.capacity() << endl;
cin>>s2;
cout << s2<<endl;
cout << "流插入后:" << s2.size() << " " << s2.capacity() << endl;
}
OK!!到这里string的基本功能已经实现了!!如果您觉得有所收获,记得点赞收藏+关注哦!!谢谢!!!
咱下期见!!!