特殊顺序表——string模拟实现
我们之前学习了vector和list的模拟实现,今天我们学习一个特别一点的顺序表,string,string开发出来的时间比STL还要早,稍微设计的有点繁琐,我们主要实现的增删查改的功能。
文档查看
按照惯例我们先来查看文档:
成员函数:
具体的可以点击相关连接:
https://legacy.cplusplus.com/reference/string/string/?kw=string
迭代器
我们先做一些基础工作:
namespace My_string
{
class string
{
private:
char* str; //字符串
size_t size; //有效数据个数
size_t capacity; //容量
};
}
我们在学组件时,最重要的就是迭代器,我们来实现一下:
namespace My_string
{
class string
{
typedef char* itreator; //非const迭代器
typedef const char* const_itreator; //const迭代器
itreator begin()
{
return _str;
}
itreator end()
{
return _str + _size;
}
const_itreator begin() const
{
return _str;
}
const_itreator end() const
{
return _str + _size;
}
private:
char* _str; //字符串
size_t _size; //有效数据个数
size_t _capacity; //容量
};
}
构造函数
//构造函数
string(const char* str="")//缺省参数给空字符串默认有一个\0
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];
//开空间多开一个,存放\0
strcpy(_str, str);
}
测试一下:
拷贝构造和赋值重载
void Swap(string& str)
{
swap(_str, str._str);
swap(_size, str._size);
swap(_capacity, str._capacity);
}
//拷贝构造
string(const string& str)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
//现代写法
string tmp(str);
Swap(tmp);
}
//赋值重载
string& operator=(string tmp)
{
Swap(tmp);
return *this;
}
析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
c_str ( )
我们目前如果想要打印字符串,我们还无法做到,因为我们还没有重载自己的流插入和流提取,但是string有一个c_str ( ) 的接口。
c_str ( ) 的返回值是一个指针,指向的是string的成员char* str。
//c.str()
const char* c_str() const
{
return _str;
}
现在可以尝试一下打印一下初始化过的string。
void test1()
{
string s("hello world");
cout << s.c_str() << endl;
}
reserve
reserve改变的是容量大小,我们之后写的插入函数,要频繁进行扩容,我们先把这个函数实现一下:
//reserve
void reserve(size_t n = 0)
{
if (n > _capacity)
{
//开辟新空间
char* tmp = new char[n + 1];
//多开一个存放一个\0
strcpy(tmp, _str);
//删除原有空间
delete[] _str;
//指向新空间
_str = tmp;
_capacity = n;
}
}
push_back ( )
push_back ( )是我们的老朋友了:
//push_back ( )
string& push_back(char c)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 :
2 * _capacity);
}
_str[_size++] = c;
_str[_size] = '\0';//最后放一个\0
return *this;
}
测试一下:
void test1()
{
string s("hello world");
cout << s.c_str() << endl;
s.push_back('F');
cout << s.c_str() << endl;
}
append
我们不仅可以在最后追加字符,我们还可以在最后追加字符串:
//append
string& append(const char* str)
{
//扩容
size_t len = strlen(str);
if (_size == _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
return *this;
}
测试一下:
void test1()
{
string s("hello world");
cout << s.c_str() << endl;
s.push_back('F');
cout << s.c_str() << endl;
s.append("My love");
cout << s.c_str() << endl;
}
重载+=
我们发现push_back和append都是追加东西,而我们可以重载+=,实现相同的功能:
string& operator+=(char c)
{
return push_back(c);
}
string& operator+=(const char* str)
{
return append(str);
}
string& operator+=(const string& str)
{
return append(str._str);
}
测试一下:
void test1()
{
string s("hello world");
cout << s.c_str() << endl;
//s.push_back('F');
s += 'F';
cout << s.c_str() << endl;
//s.append("My love");
s += "My Love";
cout << s.c_str() << endl;
string s1("Forever");
s += s1;
cout << s.c_str() << endl;
}
重载 [ ]
//重载 [ ]
char& operator[](size_t pos) //非const版本
{
assert(pos <= _size);
return _str[pos];
}
const char& operator[](size_t pos) const //const版本
{
assert(pos <= _size);
return _str[pos];
}
测试一下:
void test2()
{
string s("Forever");
cout << s[5] << endl;
}
insert
我们实现两个版本,一个插入字符,一个插入字符串
//insert 字符
string& insert(size_t pos,char c)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 *
_capacity);
}
int end = _size;
//挪动数据
while (end >=pos)
{
_str[end + 1] = _str[end];
--end;
}
_str[pos] = c;
++_size;
return *this;
}
测试一下:
void test2()
{
string s("Forever");
s.insert(0, 'P');
cout << s.c_str()<< endl;
}
然后:
为什么呢?我们来看一下报错信息:
有符号与无符号不匹配?大家还记得整型提升吗?我们end是int类型,pos是size_t类型,当着两个类型相比时,int会自动提升为size_t,为无符号类型,就意味着end变为0之后,就直接变为最大整数。
虽然调试的时候显示的是-1,但这时候已经发生了整型提升,所以这时候的end是无符号整型,肯定比pos大。
解决办法就是pos强转为int:
//insert 字符
string& insert(size_t pos,char c)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 *
_capacity);
}
int end = _size;
//挪动数据
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
--end;
}
_str[pos] = c;
++_size;
return *this;
}
//insert 字符串
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
int end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
void test2()
{
string s("Forever");
s.insert(0, 'P');
s.insert(0, "angle is bad");
cout << s.c_str()<< endl;
}
clear
//clear
void clear()
{
_str[0] = '\0';
_size = 0;
}
erase
我们发现一个新的默认参数:npos,我们发现它是这样定义的:
size_t npos = -1;
这时候大家就会很疑惑-1怎么就是size_t呢?size_t是无符号整型,意思就是-1也是被当做无符号整数,而且是最大的无符号整数。上面的意思就是,如果不给定删除长度,就按npos的长度去删除。
我们去定义一下:
我们实现一下erase:
//erase
string& erase(size_t pos,size_t len = npos)
{
if (len == pos || pos + len > _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
int end = pos + len;
while (end <= _size)
{
_str[pos++] = _str[end++];
}
_size -= len;
}
return *this;
}
resize
const string& resize(size_t n, char ch = '\0') //返回值不能被修改
{
if (n < _size)
{
_str[n] = '\0';
_size = n;
}
else
{
reserve(n);
while (_size < n)
{
_str[_size++] = ch;
}
_str[_size] = '\0';
}
return *this;
}
substr(截取子串)
//截取字串
string substr(size_t pos,size_t len = npos)
{
string s;
if (len ==npos || pos + len > _size)
{
while (pos <= _size)
{
s.push_back(_str[pos++]);
}
}
else
{
int end = pos + len; //结束位置
while (pos < end)
{
s.push_back(_str[pos++]);
}
}
return s;
}
find
find是查找指定字符或字符串的位置。
//查找指定字符
size_t find(char ch, size_t pos = 0)
{
for (int i = pos; i < _size; i++)
{
if (ch == _str[i])
{
return i;
}
}
return npos;
}
//查找指定字符串
size_t find(const char* str, size_t pos = 0)
{
const char* p = strstr(_str, str);
if (p)
{
return p - _str;
}
else
{
return npos;
}
}
重载流插入和流提取( >> , << )
我们现在的流插入和流提取无法针对我们自定义类型的string,我们得重载一下流插入和流提取:
//流输出
ostream& operator<<(ostream& out, const string& str)
{
for (auto ch : str)
{
out << ch;
}
return out;
}
测试一下:
void test3()
{
string s("Forever");
//string s1(s.erase(0, 3));
//cout << s1.c_str() << endl;
//s.resize(23, 'B');
//string s2(s.substr(3, 2));
//cout << s2.c_str() << endl;
cout << s << endl;;
}
//流输入
istream& operator>>(istream& in, string& str)
{
//为了减少扩容次数,首先将输入的字符存储在一个临时数组中
//达到某一标准后,再放入str
char buff[129];
size_t i = 0;
char ch;
ch = in.get();
while (ch != '\n')
{
buff[i++] = ch;
if (i == 128)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = in.get();
}
if (i != 0)
{
buff[i] = '\0';
str += buff;
}
return in;
}
测试一下:
但这里出现了一个东西get是什么东西?
get可以提取到每一个字符,这很关键,如果我们用**>>**来输入,会捕捉不到空格和换行符。
我们来测试一下,首先我们将get换为>>
//流输入
istream& operator>>(istream& in, string& str)
{
//为了减少扩容次数,首先将输入的字符存储在一个临时数组中
//达到某一标准后,再放入str
char buff[129];
size_t i = 0;
char ch;
//ch = in.get();
in >> ch;
while (ch != '\n')
{
buff[i++] = ch;
if (i == 128)
{
buff[i] = '\0';
str += buff;
i = 0;
}
//ch = in.get();
in >> ch;
}
if (i != 0)
{
buff[i] = '\0';
str += buff;
}
return in;
}
空格被忽略了!
我们再换回来:
//流输入
istream& operator>>(istream& in, string& str)
{
//为了减少扩容次数,首先将输入的字符存储在一个临时数组中
//达到某一标准后,再放入str
char buff[129];
size_t i = 0;
char ch;
ch = in.get();
//in >> ch;
while (ch != '\n')
{
buff[i++] = ch;
if (i == 128)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = in.get();
//in >> ch;
}
if (i != 0)
{
buff[i] = '\0';
str += buff;
}
return in;
}
附上源码:
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace My_string
{
class string
{
public:
typedef char* itreator; //非const迭代器
typedef const char* const_itreator; //const迭代器
itreator begin()
{
return _str;
}
itreator end()
{
return _str + _size;
}
const_itreator begin() const
{
return _str;
}
const_itreator end() const
{
return _str + _size;
}
//构造函数
string(const char* str="")//缺省参数给空字符串默认有一个\0
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];
//开空间多开一个,存放\0
strcpy(_str, str);
}
void Swap(string& str)
{
swap(_str, str._str);
swap(_size, str._size);
swap(_capacity, str._capacity);
}
//拷贝构造
string(const string& str)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(str._str);
Swap(tmp);
}
template<class InputItreator>
string(InputItreator first, InputItreator end)
{
while (first != end)
{
push_back(*first);
++first;
}
}
const string& resize(size_t n, char ch = '\0') //返回值不能被修改
{
if (n < _size)
{
_str[n] = '\0';
_size = n;
}
else
{
reserve(n);
while (_size < n)
{
_str[_size++] = ch;
}
_str[_size] = '\0';
}
return *this;
}
//截取字串
string substr(size_t pos,size_t len = npos)
{
string s;
if (len ==npos || pos + len > _size)
{
while (pos <= _size)
{
s.push_back(_str[pos++]);
}
}
else
{
int end = pos + len;
while (pos < end)
{
s.push_back(_str[pos++]);
}
}
return s;
}
//赋值重载
string& operator=(string tmp)
{
Swap(tmp);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//insert 字符
string& insert(size_t pos,char c)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 *
_capacity);
}
int end = _size;
//挪动数据
while (end >= (int)pos)
{
_str[end + 1] = _str[end];
--end;
}
_str[pos] = c;
++_size;
return *this;
}
//insert 字符串
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
int end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
//erase
string& erase(size_t pos,size_t len = npos)
{
if (len == pos || pos + len > _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
int end = pos + len;
while (end <= _size)
{
_str[pos++] = _str[end++];
}
_size -= len;
}
return *this;
}
//查找指定字符
size_t find(char ch, size_t pos = 0)
{
for (int i = pos; i < _size; i++)
{
if (ch == _str[i])
{
return i;
}
}
return npos;
}
//查找指定字符串
size_t find(const char* str, size_t pos = 0)
{
const char* p = strstr(_str, str);
if (p)
{
return p - _str;
}
else
{
return npos;
}
}
//重载 [ ]
char& operator[](size_t pos) //非const版本
{
assert(pos <= _size);
return _str[pos];
}
const char& operator[](size_t pos) const //const版本
{
assert(pos <= _size);
return _str[pos];
}
string& operator+=(char c)
{
return push_back(c);
}
string& operator+=(const char* str)
{
return append(str);
}
string& operator+=(const string& str)
{
return append(str._str);
}
//push_back ( )
string& push_back(char c)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 :
2 * _capacity);
}
_str[_size++] = c;
_str[_size] = '\0';//最后放一个\0
return *this;
}
//append
string& append(const char* str)
{
//扩容
size_t len = strlen(str);
if (_size == _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
return *this;
}
//reserve
void reserve(size_t n = 0)
{
if (n > _capacity && _str != nullptr)
{
//开辟新空间
char* tmp = new char[n + 1];
//多开一个存放一个\0
strcpy(tmp, _str);
//删除原有空间
delete[] _str;
//指向新空间
_str = tmp;
_capacity = n;
}
else
{
_str = new char[n + 1];
_size = 0;
_capacity = n;
}
}
//c.str()
const char* c_str() const
{
return _str;
}
//clear
void clear()
{
_str[0] = '\0';
_size = 0;
}
private:
char* _str; //字符串
size_t _size; //有效数据个数
size_t _capacity; //容量
public:
const static size_t npos;
};
const size_t string::npos = -1;
//流输出
ostream& operator<<(ostream& out, const string& str)
{
for (auto ch : str)
{
out << ch;
}
return out;
}
//流输入
istream& operator>>(istream& in, string& str)
{
//为了减少扩容次数,首先将输入的字符存储在一个临时数组中
//达到某一标准后,再放入str
char buff[129];
size_t i = 0;
char ch;
ch = in.get();
//in >> ch;
while (ch != '\n')
{
buff[i++] = ch;
if (i == 128)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = in.get();
//in >> ch;
}
if (i != 0)
{
buff[i] = '\0';
str += buff;
}
return in;
}
void test1()
{
string s("hello world");
cout << s.c_str() << endl;
//s.push_back('F');
s += 'F';
cout << s.c_str() << endl;
//s.append("My love");
s += "My Love";
cout << s.c_str() << endl;
string s1("Forever");
s += s1;
cout << s.c_str() << endl;
}
//void test2()
//{
// string s("Forever");
// s.insert(0, 'P');
// s.insert(0, "angle is bad");
// cout << s.c_str()<< endl;
//}
void test3()
{
string s("Forever");
//string s1(s.erase(0, 3));
//cout << s1.c_str() << endl;
//s.resize(23, 'B');
//string s2(s.substr(3, 2));
//cout << s2.c_str() << endl;
cin >> s;
cout << s << endl;;
}
}