string的模拟实现
Posted aaaaaaaWoLan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了string的模拟实现相关的知识,希望对你有一定的参考价值。
文章目录
拷贝构造和赋值重载的传统写法vs现代写法
传统写法
//传统写法
//拷贝构造
string(const string& s1)
{
assert(s1._str);
_size = _capacity = strlen(s1._str);
_str = new char[_capacity + 1];
strcpy(_str, s1._str);
}
//赋值重载
string& operator= (const string& s1)
{
//深拷贝
delete[]_str;
_size = _capacity = strlen(s1._str);
_str = new char[_capacity + 1];
strcpy(_str, s1._str);
return *this;
}
现代写法:
//现代写法
//拷贝构造
string(const string& s1)
:_str(nullptr)
{
assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝
string tmp(s1._str);
std::swap(_str, tmp._str);
_size = _capacity = strlen(_str);
}
//赋值重载
string& operator= (const string& s1)
{
string tmp(s1);
std::swap(_str, tmp._str);
_capacity = _size = strlen(_str);
return *this;
}
拷贝构造和赋值重载的交换
//实现直接交换类的swap,库里的swap也能完成,但要完成三次深拷贝,效率低下
//系统提供的:
/* template <class T> void swap(T&a, T&b)
{
//string的拷贝,三次深拷贝
T c(a);
a = b;
b = c;
}
*/
//自己实现的
void swap(string& s)
{
//swap使用后,在局部作用域会默认只使用一种参数类型的交换,所以要用::来表明它是全局的,不受局部限制
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
拷贝构造和赋值重载的现代写法可以复用swap
//现代写法
//拷贝构造
string(const string& s1)
:_str(nullptr)
{
assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝
string tmp(s1._str);
swap(tmp);
}
//赋值重载
string& operator= (const string& s1)
{
string tmp(s1);
swap(tmp);
return *this;
}
范围for
如果我们把迭代器的名称一改,由begin->Begin,end->End,范围for就实现不了了
所以说范围for的底层是由迭代器实现的
“增”的操作(从尾部添加)
字符:push_back
字符串:append
还有一个很好用的operator+=,可以复用上面两个函数
既然要添加字符,就不得不涉及到增容:reserve
reserve实现
//reserve
void reserve(size_t capacity)
{
if (capacity > _capacity)
{
char* tmp = new char[capacity + 1];
strncpy(tmp, _str, _size + 1);//要用strncpy或memcpy拷贝,如果用strcpy,可能源字符串是"hello\\0\\0\\0\\0",这样就会导致拷贝的内容不一致
delete[] _str;
_str = tmp;
_capacity = capacity;
}
}
既然有reserve,那么也得提到resize
resize的实现:
//resize
void resize(int n, char x = '\\0')
{
//有三种情况:
//1.n <= _size,将n赋值给_size,_str[_size] = '\\0'
//2._size < n <= _capacity,循环将多余的位置设置为'\\0'
//3.n > _capacity,先reserve(n),再将多余的位置设置为'\\0'
if (n <= _size)
{
_size = n;
_str[_size] = '\\0';
}
else if (n <= _capacity)
{
int end = n;
while (end > _size)
{
_str[end] = '\\0';
end--;
}
_size = n;
}
else
{
reserve(n);
int end = n;
while (end > _size)
{
_str[end] = '\\0';
--end;
}
_size = n;
}
}
“改”的实现:insert
特殊情况:pos和是size_t的类型且pos为0时
<<、>>及inline的重载
<<容易实现,>>的输入遇到空格和回车时会停止读取,所以采用循环读取字符的方式,当字符为空格或回车时,就停止输入,可以用cin.get()接口来实现字符的读取,类似getchar
getline的原理与>>类似,遇到回车才停止输入。
注意:>>和getline的输入是覆盖输入的,所以输入前需要用clear接口清空
总体实现:
#pragma once
#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;
namespace ysj{
class string {
public:
typedef char* iterator;
//*****************************************************************************************************************************
//构造函数
string(const char* str = "")//默认缺省值不给nullptr,因为下面要将str作为strlen的参数,会引发错误
{
_size = _capacity = strlen(str);//_capacity是指有效容量,不包括\\0,但实际开的空间要多一个字节容纳\\0
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
_capacity = _size = 0;
}
//传统写法
//拷贝构造
/*string(const string& s1)
{
assert(s1._str);
_size = _capacity = strlen(s1._str);
_str = new char[_capacity + 1];
strcpy(_str, s1._str);
}*/
//赋值重载
//string& operator= (const string& s1)
//{
// //深拷贝
// delete[]_str;
//
// _size = _capacity = strlen(s1._str);
// _str = new char[_capacity + 1];
// strcpy(_str, s1._str);
// return *this;
//}
//现代写法
//拷贝构造
string(const string& s1)
:_str(nullptr)
{
assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝
//string tmp(s1._str);
//std::swap(_str, tmp._str);
//_size = _capacity = strlen(_str);
string tmp(s1._str);
swap(tmp);
}
//赋值重载
string& operator= (const string& s1)
{
/*string tmp(s1);
std::swap(_str, tmp._str);
_capacity = _size = strlen(_str);*/
string tmp(s1);
swap(tmp);
return *this;
}
//实现直接交换类的swap,库里的swap也能完成,但要完成三次深拷贝,效率低下
/* template <class T> void swap(T&a, T&b)
{
//string的拷贝,三次深拷贝
T c(a);
a = b;
b = c;
}
*/
void swap(string& s)
{
//swap使用后,在局部作用域会默认只使用一种参数类型的交换,所以要用::来表明它是全局的,不受局部限制
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//查
//1.[]重载
//可读可写返回
char& operator[](size_t pos)
{
assert(pos <= size());
return _str[pos];
}
//只读返回
char& operator[](size_t pos)const
{
assert(pos <= size());
return _str[pos];
}
//2.迭代器
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
//const迭代器
const iterator begin()const
{
return _str;
}
const iterator end()const
{
return _str + _size;
}
//返回string的有效字符数字
size_t size()
{
return _size;
}
//3.范围for,其本质就是用迭代器实现的
//万一operatoe[]想以只读的方式返回,size()也要是const的
size_t size()const
{
return _size;
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//增
//1.push_back
void push_back(char x)
{
//判断增容,若需要增容,使用reserve增容
if (_size == _capacity)
{
int capacity = (_capacity == 0 ? 4 : 2 * _capacity);
reserve(capacity);
}
_str[_size] = x;
_str[_size + 1] = '\\0';
++_size;
}
//2.append
void append(const char* str)
{
int len = strlen(str) + _size;
if (len > _capacity)
{
reserve(len);
}
strncpy(_str + _size, str, strlen(str) + 1);
_size = len;
_str[_size] = '\\0';
}
//3.operator+=
//可复用append和push_back的代码
//字符
string& operator+=(char x)
{
push_back(x);
return *this;
}
//字符串
string& operator+=(const char* str)
{
append(str);
return *this;
}
//reserve
void reserve(size_t capacity)
{
if (capacity > _capacity)
{
char* tmp = new char[capacity + 1];
strncpy(tmp, _str, _size + 1);//要用strncpy或memcpy拷贝,如果用strcpy,可能源字符串是"hello\\0\\0\\0\\0",这样就会导致拷贝的内容不一致
delete[] _str;
_str = tmp;
_capacity = capacity;
}
}
//resize
void resize(int n, char x = '\\0')
{
//有三种情况:
//1.n <= _size,将n赋值给_size,_str[_size] = '\\0'
//2._size < n <= _capacity,循环将多余的位置设置为'\\0'
//3.n > _capacity,先reserve(n),再将多余的位置设置为'\\0'
if (n <= _size)
{
_size = n;
_str[_size] = '\\0';
}
else if (n <= _capacity)
{
int end = n;
while (end > _size)
{
_str[end] = '\\0';
end--;
}
_size = n;
}
else
{
reserve(n);
int end = n;
while (end > _size)
{
_str[end] = '\\0';
--end;
}
_size = n;
}
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//改,insert,在pos之前插入
//字符
void insert(char x, size_t pos)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(2 * _capacity);
}
size_t end = _size + 1;//如end等于_size+1,当pos等于0时,下面的while循环中,end永远不会小于0,就陷入了死循环
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = x;
++_size;
}
//字符串
void insert(const char* s, size_t pos)
{
assert(pos <= _size);
int len = strlen(s);
if (len + _size > _capacity)
{
reserve(len + _size);
}
size_t end = _size + 1;//如end等于_size+1,当pos等于0时,下面的while循环中,end永远不会小于0,就陷入了死循环
while (end > pos)
{
_str[end + len - 1] = _str[end - 1];
--end;
}
strncpy(_str + pos, s, strlen(s));
_size += len;
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//删,erase,从pos位置开始删除
///默认从开头删
string& erase(size_t pos = 0, size_t len = npos)//静态缺省变量npos,表示当没有给出删除的位置时,默认从npos开始,也就是字符串的末尾
{
assert(pos < _size);
if (len > _size - pos)//剩余字符长度小于删除字符长度
{
//将从pos位置开始的之后所有字符都删除
_str[pos] = '\\0';
_size = pos;
}
else//向前挪
{
strncpy(_str + pos, _str + pos + len, strlen(_str + pos + len) + 1);
_size -= len;
}
return *this;
}
//*********************************************************************************************************************
//*********************************************************************************************************************
//find、rfind的实现
//find
//字符
size_t find(char x, size_t pos = 0)//找到了返回下标,没找到返回npos
{
for (int i = pos; i < _size; ++i)
{
if (x == _str[i])
return i;
}
return npos;
}
//字符串
size_t find(const char*s, size_t pos = 0)
{
char* ret = nullptr;
//用strstr查找
for (int i = pos; i < _size; ++i)
{
ret = strstr(_str + i, s);
if (ret)
{
return ret - _str;
}
}
return npos;
}
//refind
//字符
size_t rfind(char x, size_t pos = npos)//默认从最后开始查找
{
if (pos >= _size)
{
pos = _size - 1;
}
for (int i = pos; i >= 0; --i)
{
if (_str[i] == x)
{
return i;
}
}
return npos;
}
//字符串
size_t rfind(const char* s, size_t pos = npos)
{
if (pos >= _size)
{
pos = _size - 1;
}
char* ret = nullptr;
for (int i = pos; i >= 0; --i)
{
ret = strstr(_str + i, s);
if (ret)
{
return ret - _str;
}
}
return npos;
}
//*********************************************************************************************************************
//clear重载,清空字符串
void clear()
{
_size = 0;
_str[0] = '\\0';
}
//*********************************************************************************************************************
//>的重载
bool operator>(const string& s)const
{
assert(s._str);
return strcmp(_str, s._str) > 0;
}
//==的重载
bool operator==(const string& s)<以上是关于string的模拟实现的主要内容,如果未能解决你的问题,请参考以下文章