实现了STL库中string的主要函数接口,简单底层实现string。
namespace byte
{
//模拟实现string类
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
//默认成员函数
string(const char* str = ""); //构造函数
string(const string& s); //拷贝构造函数
string& operator=(const string& s); //赋值运算符重载函数
~string(); //析构函数
//迭代器相关函数
iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end()const;
//容量和大小相关函数
size_t size();
size_t capacity();
void reserve(size_t n);
void resize(size_t n, char ch = '\0');
bool empty()const;
//修改字符串相关函数
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char* str);
string& erase(size_t pos, size_t len);
void clear();
void swap(string& s);
const char* c_str()const;
//访问字符串相关函数
char& operator[](size_t i);
const char& operator[](size_t i)const;
size_t find(char ch, size_t pos = 0)const;
size_t find(const char* str, size_t pos = 0)const;
size_t rfind(char ch, size_t pos = npos)const;
size_t rfind(const char* str, size_t pos = 0)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;
bool operator!=(const string& s)const;
private:
char* _str; //存储字符串
size_t _size; //记录字符串当前的有效长度
size_t _capacity; //记录字符串当前的容量
static const size_t npos; //静态成员变量(整型最大值)
};
const size_t string::npos = -1;
//<<和>>运算符重载函数
istream& operator>>(istream& in, string& s);
ostream& operator<<(ostream& out, const string& s);
istream& getline(istream& in, string& s);
}
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include
#include
#include
using namespace std;
//命名空间域:与库里面的string类进行隔离,防止命名冲突
namespace byte
{
class string
{
public:
//默认成员函数
//注意缺省值给"\0",有两个斜杠零
//所以这里缺省值给"",只有一个斜杠零,定义空串时,new出来的一个空间也是为斜杠零准备的
/*string()
{
_str = new char[1];
_str[0] = '\0';
_size = _capacity = 0;
}*/
//构造函数
string(const char* str = "")//缺省参数,如果定义string s;即默认定义一个空字符串
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];//_capacity只是有效字符的长度,+1为'\0'开辟空间
//将要构造函数拷贝到_str中
strcpy(_str, str);
}
void swap(string& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
//拷贝构造函数
//传统写法
/*string(const string& str)
{
_str = new char[str._size + 1];
_size = str._size;
_capacity = str._capacity;
strcpy(_str, str._str);
}*/
//现代写法
string(const string& s)
:_size(0)
,_capacity(0)
,_str(nullptr)
{
//先构造一个临时对象
string tmp(s._str);
/*(*this).swap(tmp);*/
swap(tmp);
}
//赋值重载
//string s2=s1;两个对象已存在,分别有自己的空间和大小
//string& operator=(const string& s)
//{
// if (this != &s)//这里是取地址的意思
// {
// delete[] _str;
// _str = new char[s._capacity + 1];
// _size = s._size;
// _capacity = s._capacity;
// strcpy(_str, s._str);
// }
// return *this;//支持连续赋值
//}
//现代写法
string& operator=(const string& s)
{
if (this != &s)
{
string tmp(s._str);
swap(tmp);
}
return *this;//支持连续赋值
}
//析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//类对象变量的操作
//输出,要转换成字符串常量
const char* c_str() const
{
return _str;
}
//返回string类对象的大小
size_t size() const
{
return _size;
}
//返回string类对象的容量
size_t capacity() const
{
return _capacity;
}
void reserve(size_t n)//相当于realloc
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize( size_t n,char ch='\0')//缺省参数用'\0'
{
//1.如果resize(n),n<_size,相当于删除字符
if (n <=_size)
{
_str[n] = '\0';
_size = n;
}
else//相当于扩容,多余部分如果没有传参,默认用'\0'初始化
{
if (n > _capacity)
{
reserve(n);
}
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_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];
}
//正向查找第一个匹配字符的下标:不传pos的话,默认从0开始查找
size_t find(char ch, size_t pos = 0) const
{
assert(pos < _size);//下标为_size的地方,为'\0'
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) const
{
assert(pos < _size);
const char* ret = strstr(_str + pos, str);//从原字符串的pos位置开始查找子串
//找到的话返回首元素地址
if (ret)
{
return ret - _str;//ret指向找到的子串的首元素,保存着首元素地址,相减得到所找字符串的下标
}
else
{
return npos;//找不到
}
}
//反向查找第一个匹配的字符
size_t rfind(char ch, size_t pos = npos)
{
string tmp(*this); //拷贝构造对象tmp
reverse(tmp.begin(), tmp.end()); //调用reverse逆置对象tmp的C字符串
if (pos >= _size) //所给pos大于字符串有效长度
{
pos = _size - 1; //重新设置pos为字符串最后一个字符的下标
}
pos = _size - 1 - pos; //将pos改为镜像对称后的位置
size_t ret = tmp.find(ch, pos); //复用find函数
if (ret != npos)
return _size - 1 - ret; //找到了,返回ret镜像对称后的位置
else
return npos; //没找到,返回npos
}
//反向查找第一个匹配的字符串
size_t rfind(const char* str, size_t pos = npos)
{
string tmp(*this); //拷贝构造对象tmp
reverse(tmp.begin(), tmp.end()); //调用reverse逆置对象tmp的C字符串
size_t len = strlen(str); //待查找的字符串的长度
char* arr = new char[len + 1]; //开辟arr字符串(用于拷贝str字符串)
strcpy(arr, str); //拷贝str给arr
size_t left = 0, right = len - 1; //设置左右指针
//逆置字符串arr
while (left < right)
{
::swap(arr[left], arr[right]);
left++;
right--;
}
if (pos >= _size) //所给pos大于字符串有效长度
{
pos = _size - 1; //重新设置pos为字符串最后一个字符的下标
}
pos = _size - 1 - pos; //将pos改为镜像对称后的位置
size_t ret = tmp.find(arr, pos); //复用find函数
delete[] arr; //销毁arr指向的空间,避免内存泄漏
if (ret != npos)
return _size - ret - len; //找到了,返回ret镜像对称后再调整的位置
else
return npos; //没找到,返回npos
}
//迭代器相关函数
//迭代器实际就是指针的typedef出来的别名
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;//char*指针指向字符串第一个字符
}
iterator end()
{
return _str + _size;//char* 指针指向字符串最后一个字符,即'\0'
}
//const迭代器
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
//修改字符串相关函数
void push_back(const char ch)
{
//插入字符时,如果容量不够,就reserve马上扩容
//以二倍形式增容,避免频繁扩容
if (_size == _capacity)
{
int newcapacity = _capacity == 0 ? 4 : 2 * _capacity;//避免空串,容量大小为0的情况
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str) + _size;
if (len > _capacity)
{
reserve(len);
}
strcpy(_str + _size, str);//拷贝追加到_str+_size后面,刚好也把'\0'拷贝过来了
_size = len;
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
//在pos位置插入字符
string& insert(size_t pos,char ch)
{
assert(pos<=_size);
//检查容量,判断是否需要扩容
if (_size == _capacity)
{
int newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
reserve(newcapacity);
}
size_t end = _size+1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
return *this;
}
//在pos位置插入字符串
string& insert(size_t pos,const char* str)
{
assert(pos<=_size);
int len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
size_t end = _size + len;
//注意end>=0不行,--成-1,无符号整型-1是一个极大的数字会陷入死循环
while (end > pos + len-1)
{
_str[end] = _str[end - len];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
//从pos位置开始删除len个字符,npos是-1
string& erase(size_t pos, size_t len=npos)
{
assert(pos < _size);
//如果len等于-1,或者说从pos位置开始,剩下的字符小于len,不够删,就直接将pos置为'\0'pos位置后面的全部删除
size_t n = _size - pos;//从pos位置开始剩下的有效字符
if (len >= n||len==npos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
void clear()
{
//没有是否空间,只是清空数据
_size = 0;//有效字符个数清零
_str[0] = '\0';
}
bool empty()
{
return strcmp(_str, "")==0;//注意两个字符串的比较一定不能用==
}
//关系运算符重载函数
bool operator>(const string& s) const
{
return strcmp(_str, s._str) > 0;//比较的是第一个字符的ASCII码值
}
bool operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;//比较的是第一个字符的ASCII码值
}
bool operator>=(const string& s) const
{
return (*this) > s || (*this) == s;
}
bool operator<(const string& s) const
{
return !(*this >= s);
}
bool operator<=(const string& s) const
{
return !(*this > s);
}
bool operator!=(const string& s) const
{
return (*this == s);
}
private:
char* _str;
size_t _size;
size_t _capacity;
//注意在C++中,static修饰的变量不属于某个对象,而是属于整个类
const static size_t npos = -1;//const修饰的静态变量可以使用缺省值,
};
//流插入和流提取
//注意<<,>>支持内置类型,对于string自定义类,需要我们一个一个的去输出
ostream& operator<<(ostream& out, const string& str)//传过来的是对象
{
//一个字符一个字符的输出
for (auto e : str)
{
cout << e;//cout支持内置类型
}
//返回out和in的原因是为了支持:cin>>s2>>s3;
//先执行cin>>s2,将终端的输入逐个提取到str,s2的别名中,即放入s2中
//最后返回in,就相当于cin>>s3;
return out;
}
//str是【输入string类对象的别名】,这个原来是个空对象
istream& operator>>(istream& in, string& str)//对象不能用const修饰,in从终端逐个提取字符,放入str中
{
//遇到' 'or '\n',不再读取,未读取的字符在缓冲区当中
//所以每次读取要清楚数据
str.clear();
char ch = in.get();//cin的成员函数get()
while (ch != ' '&&ch != '\n')
{
str += ch;
ch = in.get();
}
return in;
}
istream& getline(istream& in, string& str)
{
str.clear();
char ch = in.get();
while (ch != '\n')
{
str += ch;
ch = in.get();
}
return in;
}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
int main()
{
/*byte::test_string1();*/
//byte::test_string2();
/*byte::test_string3();*/
/*byte::test_string4();*/
/*byte::test_string5();*/
/*byte::test_string6();*/
/*byte::test_string7();*/
byte::test_string8();
return 0;
}