一、头文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h>//断言库
#include<iostream>
#include<string>
using namespace std;
namespace wxt//写在自己的命名空间中
{
class string
{
public:
string(const char* str = "");//带参构造,给缺省处理空字符串的情况
~string();//析构
size_t size() const;//返回对象大小
size_t capacity() const;//对象容量
char& operator[](size_t pos);//对下标引用操作符的重载
const char operator[](size_t pos) const;//下标引用操作符的const重载
typedef char* iterator;//迭代器
iterator begin();//开始迭代器
iterator end();//结束迭代器
void reserve(size_t n);//对象扩容函数
void push_back(char ch);//尾插字符函数
void append(const char* str);//尾插字符串函数
string& operator+=(char ch);//类似尾插字符
string& operator+=(const char* str);//类似尾插字符串
void insert(size_t pos, char ch);//任意位置插入字符
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;
void clear();//情况对象数据
const char* str() const;//以字符串返回对象
typedef char* iterator;//针对const对象的迭代器
iterator begin() const;
iterator end() const;
//——————————————————————————————
//——————————————————————————————
void insert(size_t pos, const char* str);//任意插入字符串
void erase(size_t pos, size_t len = std::string::npos);//删除字符或字符串
void rsize(size_t n, char ch = '\0');//设置对象大小
size_t find(char ch, size_t pos = 0) const;//查找对象中指定字符
size_t find(const char* str, size_t pos = 0) const;//查找对象中指定字符串
string substr(size_t pos = 0, size_t len = std::string::npos) const;//截取对象部分元素并作为一个新的元素
private:
char* _str;
size_t _size;
size_t _capacity;
};
ostream& operator<<(ostream& out, const string& s);//流输出重载
istream& operator>>(istream& in, string& s);//流提取重载
}
二、函数具体实现解析:
2.1构造函数:
- 可以有参也可以无参
- 声明给缺省值,定义不给缺省值。使用一个不包含任何数据的双引号作为缺省值,是因为双引号可以表示一个字符串,自动包含一个'\0'。就可以不用写无参构造。
- 初始化列表完成对大小和容量的初始化,函数体内完成空间的开辟和内容的拷贝。
//声明
string(const char* str = "");//带参构造,给缺省处理空字符串的情况
//定义
wxt::string::string(const char* str)//给一个缺省值,处理传空字符串的情况
:_size(strlen(str))//大小开辟为传来的字符串的长度
, _capacity(_size)//容量设置为字符串的大小
{
_str = new char[_capacity + 1];//多开辟一个字节空间,存放\0
strcpy(_str, str);//将空字符串拷贝过去
}
2.2析构函数
//声明
~string();
//定义
wxt::string::~string()
{
delete[] _str;//释放对象在堆上动态开辟的空间
_str = nullptr;//将指向堆上空间的指针置空
_size = _capacity = 0;//将大小和容量置空
}
2.3返回对象的大小和容量:
- 加上const修饰this指针,使得值不可被修改。
//声明
size_t size() const;
size_t capacity() const;
//定义
size_t wxt::string::size() const//返回对象大小,加const修饰*this不被修改
{
return _size;
}
size_t wxt::string::capacity() const//返回容量大小,加const成员修饰this指针
{
return _capacity;
}
2.4下标引用操作符的重载:
- 作用是可以像操作字符串一样操作string对象
- 使用引用返回,就可以间接修改对象指定下标处的元素。
- 加上断言,处理错误传递下标的情况
//声明
char& operator[](size_t pos);
//定义
char& wxt::string::operator[](size_t pos)//传引用返回,可以修改指定下标处字符
{
assert(pos < _size);//断言下标为合法位置
return _str[pos];//返回对象下标
}
2.5下标引用操作符的const版本重载:
- const版本主要用于下标操作const修饰的string对象
- 由于和非const版本的参数完全相同,需要加上const成员修饰this指针构成函数重载。
//声明
const char operator[](size_t pos) const;
//定义
const char wxt::string::operator[](size_t pos) const
//前面的const修饰函数,表示该函数为const修饰的string对象使用
//后面的const修饰this指针,与上一个[]的运算符重载构成函数重载
{
assert(pos < _size);
return _str[pos];
}
2.6迭代器:
- 迭代器实际返回的是一个指针,所以将char*类型的指针,重命名为iterator
- begin返回_str即可,代表首元素的地址
//声明
typedef char* iterator;
iterator begin();
iterator end();
//定义
wxt::string::iterator wxt::string::begin()
{
return _str;//返回字符串名字,相当于首元素地址
}
wxt::string::iterator wxt::string::end()
{
return _str + _size;//返回最后一个字符后一个的地址
//相当于返回\0的地址,原string对象end-1才是最后一个字符地址
}
2.7针对const对象的迭代器:
- 思路同上,加上const成员修饰this指针,使得构成函数重载
//声明
typedef char* iterator;
iterator begin() const;
iterator end() const;
//定义
wxt::string::iterator wxt::string::begin() const
{
return _str;
}
wxt::string::iterator wxt::string::end() const
{
return _str + _size;
}
2.8预留空间函数:
- 如果要求预留的空间大于对象当前容量才进行扩容,否则不进行扩容。
- 思路就是数据的迁移,先开要求大小的新空间,将旧空间的数据迁移过去,释放旧空间,对象中指向旧空间的指针指向新空间,更新对象的容量。
//声明
void reserve(size_t n);
//定义
void wxt::string::reserve(size_t n)
{
if (n > _capacity)//判断,如果预扩容大小大于原容量才扩容
{
char* tmp = new char[n + 1];//开辟所需空间
strcpy(tmp, _str);//拷贝原空间数据到开辟好的空间
delete[] _str;//释放原空间
_str = tmp;//原空间指针指向新空间
_capacity = n;//更新对象容量
}
}
2.9尾插:
- 首先判断是否需要扩容,需要则先扩容再处理数据的插入
//声明
void push_back(char ch);
//定义
void wxt::string::push_back(char ch)
{
if (_size == _capacity)//如果大小等于容量就扩容
{
reserve(_capacity == 0 ? 4 : _capacity * 2);//初始开4字节空间,之后双倍扩容,可以自己定义
}
_str[_size] = ch;//字符插入到对象末尾
_size++;//对象大小加1
_str[_size] = '\0';//对象末尾空间设置为'\0'
}
2.10尾插字符串
- 思路:先判断是否需要扩容,处理好容量问题就可以将字符串拷贝到对象末尾。最后更新对象的大小即可
//声明
void append(const char* str);
//定义
void wxt::string::append(const char* str)
{
size_t len = strlen(str);//求字符串长度
if (len + _size > _capacity)//判断是否需要扩容
{
reserve(len + _size);
}
strcpy(_str + _size, str);//拷贝字符串到末尾
_size = _size + len;//更新对象大小
}
2.11对+=的运算符重载:
//声明
string& operator+=(char ch);
string& operator+=(const char* str);
//定义
wxt::string& wxt::string::operator+=(char ch)
{
push_back(ch);
return *this;
}
//引用返回,复用尾插
wxt::string& wxt::string::operator+=(const char* str)
{
assert(str);
append(str);
return *this;
}
2.12插入单个字符:
- 在指定位置插入单个字符。
- 首先判断要插入位置是否合法。然后判断是否需要扩容。在指定位置开始向后挪动数据。将指定字符插入指定位置。更新对象大小。
//声明
void insert(size_t pos, char ch);
//定义
void wxt::string::insert(size_t pos, char ch)
{
assert(pos <= _size);//判断插入位置是否合法
if (_capacity == _size)//判断是否需要扩容
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
size_t end = _size + 1;
while (end > pos)//挪动数据,从pos位置向后挪动一位
//同时会挪动'\0'!
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;//插入数据
_size++;//更新对象大小
}
2.13插入字符串
//声明
void insert(size_t pos, const char* str);
//定义
void wxt::string::insert(size_t pos, const char* str)
{
assert(pos <= _size);//断言下标是否合法
size_t len = strlen(str);//求要插入的字符串的长度
if (_size + len > _capacity)//超过空间则扩容
{
reserve(_size + len);
}
size_t end = _size + 1;//从后向前挪动,标记最后要操作的位置
while (end > pos)//挪动数据
{
_str[end + len - 1] = _str[end - 1];
--end;
}
_size += len;//更新对象大小
strncpy(_str + pos, str, len);//使用strncpy不使用strcpy,防止将字符串的'\0'也拷贝过来
}
2.14运算符的重载:
//声明:
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;
//定义
bool wxt::string::operator<(const wxt::string& s) const
{
return strcmp(_str, s._str) < 0;
}
bool wxt::string::operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;
}
bool wxt::string::operator<=(const string& s) const
{
return (*this < s) || (*this == s);
}
bool wxt::string::operator>(const string& s) const
{
return !(*this < s);
}
bool wxt::string::operator>=(const string& s) const
{
return (*this > s) || (*this == s);
}
bool wxt::string::operator!=(const string& s) const
{
return !(*this == s);
}
2.15数据清理函数:
- 完成空间数据的清理,只是清理数据,所以只会影响对象的大小,不会影响对象的容量。
//声明
void clear();
//定义
void wxt::string::clear()
{
_size = 0;
_str[0] = '\0';
}
2.16删除指定字符或字符串
//声明
void erase(size_t pos, size_t len = std::string::npos);
//定义
void wxt::string::erase(size_t pos, size_t len)
{
assert(pos < _size);//判断下标是否合法,由于删除的是数据,所以合法的下标不能是_size
if (len == std::string::npos || len + pos >= _capacity)
{//处理全删和默认不传值的情况,不传值默认全删
_str[pos] = '\0';//直接将指定位置设置为'\0'
_size = pos;//更新对象大小,pos处为'\0',也就是_size的值
}
else
{//处理删除部分数据的情况
size_t begin = pos + len;//设置要挪动数据的起始位置
while (begin <= _size)//循环挪动数据,结束条件为起始位置和末尾数据所在位置重叠
{
_str[begin - len] = _str[begin];
++begin;
}
_size -= len;//更新对象大小
}
}
2.17更改对象大小
- 指定大小小于或等于对象大小,说明要截断数据
- 大于,则说明要扩充,使用指定字符扩充即可
//声明
void rsize(size_t n, char ch = '\0');
//定义
void wxt::string::rsize(size_t n, char ch)
{
if (n <= _size)//如果指定大小小于对象大小,说明需要删除超过指定大小意外的数据
{
_str[n] = '\0';
_size = n;
}
else
{
reserve(n);//扩容,如果n小于_capacity,不扩容。
while (_size < n)//如果n大于_size,说明需要插入指定字符
{//循环,知道n等于_size再终止
_str[_size] = ch;
++_size;
}
_str[_size] = '\0';//将_size设置为\0
}
}
2.18find系列
- 查找字符,暴力匹配即可
- 查找子串,使用strstr函数即可
//声明
size_t find(char ch, size_t pos = 0) const;
size_t find(const char* str, size_t pos = 0) const;
//定义
size_t wxt::string::find(char ch, size_t pos) const
{
//不需要使用assert断言下标是否合法
//因为,下标不合法和没有查到,都可以返回npos
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return std::string::npos;
}
//find查找字符串
size_t wxt::string::find(const char* str, size_t pos) const
{
//无需assert断言,下标不合法返回npos即可
const char* p = strstr(_str + pos, str);
if (p)
{
return p - _str;//指针减去指针,得到相差的元素个数
}
return std::string::npos;
}
2.19截取对象的一部分作为新的对象
//声明
string substr(size_t pos = 0, size_t len = std::string::npos) const;
//定义
wxt::string wxt::string::substr(size_t pos, size_t len) const
{
wxt::string s;//创建一个用于返回的对象
size_t end = pos + len;//设置结束位置
if (len == std::string::npos || len + pos >= _size)
{
len = _size - pos;//求出要截取的长度,用于之后开辟空间
end = _size;//如果是全取,更新结束位置为最后一个元素的后一个位置,也就是'\0'处
}
s.reserve(len);//一次开辟好空间
for (size_t i = pos; i < end; i++)
{
s += _str[i];//将截取的子串,一个字符一个字符的添加到要返回的对象中
}
return s;//返回子串对象
}
2.20流提取的重载:
//声明
istream& operator>>(istream& in, string& s);
//定义
istream& wxt::operator>>(istream& in, string& s)
{
s.clear();//清空对象
char ch;
ch = in.get();//使用get方法获取一个流字符
while (ch != ' ' && ch != '\n')//当获取的字符不为空格和换行时
{
s += ch;//字符尾插到对象
ch = in.get();//获取下一个字符
}
return in;//返回istream指针
}
2.21流输出的重载:
//声明
ostream& operator<<(ostream& out, const string& s);
//定义
ostream& wxt::operator<<(ostream& out, const wxt::string& s)
{
for (size_t i = 0; i < s.size(); i++)//循环打印
{
out << s[i];
}
return out;//返回istream指针
}
三、函数文件
#pragma once
#include "string.h"
//无参构造,可以被下面的带参构造+缺省的方式替代
//wxt::string::string()
// :_str(new char[1]{'\0'})//空string对象也要包含一个字符\0占位
// ,_size(0)//大小和容量都初始化为0,容量大小不包含\0在内
// ,_capacity(0)
//{}
//带参构造
wxt::string::string(const char* str)//给一个缺省值,处理传空字符串的情况
:_size(strlen(str))//大小开辟为传来的字符串的长度
, _capacity(_size)//容量设置为字符串的大小
{
_str = new char[_capacity + 1];//多开辟一个字节空间,存放\0
strcpy(_str, str);//将空字符串拷贝过去
}
//析构
wxt::string::~string()
{
delete[] _str;//释放对象在堆上动态开辟的空间
_str = nullptr;//将指向堆上空间的指针置空
_size = _capacity = 0;//将大小和容量置空
}
size_t wxt::string::size() const//返回对象大小,加const修饰*this不被修改
{
return _size;
}
size_t wxt::string::capacity() const//返回容量大小,加const成员修饰this指针
{
return _capacity;
}
char& wxt::string::operator[](size_t pos)//传引用返回,可以修改指定下标处字符
{
assert(pos < _size);//断言下标为合法位置
return _str[pos];//返回对象下标
}
const char wxt::string::operator[](size_t pos) const
//前面的const修饰函数,表示该函数为const修饰的string对象使用
//后面的const修饰this指针,与上一个[]的运算符重载构成函数重载
{
assert(pos < _size);
return _str[pos];
}
wxt::string::iterator wxt::string::begin()
{
return _str;//返回字符串名字,相当于首元素地址
}
wxt::string::iterator wxt::string::end()
{
return _str + _size;//返回最后一个字符后一个的地址
//相当于返回\0的地址,原string对象end-1才是最后一个字符地址
}
//扩容函数,思路是先判断是否需要扩容,
//需要扩容,先开辟空间,然后将原空间内容拷贝过去
//释放原空间,更新指针指向新空间,更新对象容量大小
void wxt::string::reserve(size_t n)
{
if (n > _capacity)//判断,如果预扩容大小大于原容量才扩容
{
char* tmp = new char[n + 1];//开辟所需空间
strcpy(tmp, _str);//拷贝原空间数据到开辟好的空间
delete[] _str;//释放原空间
_str = tmp;//原空间指针指向新空间
_capacity = n;//更新对象容量
}
}
//尾插,思路:
//首先判断空间是否足够,不足扩容
//足够,将字符放在末尾,将最后一位的后一位设为\0
void wxt::string::push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
//尾插字符串,思路:
//判断是否需要扩容,将字符串拷贝到对象末尾,更新对象大小
void wxt::string::append(const char* str)
{
size_t len = strlen(str);//求字符串长度
if (len + _size > _capacity)//判断是否需要扩容
{
reserve(len + _size);
}
strcpy(_str + _size, str);//拷贝字符串到末尾
_size = _size + len;//更新对象大小
}
//运算符重载+=,引用返回,复用尾插,返回对象this指针
wxt::string& wxt::string::operator+=(char ch)
{
push_back(ch);
return *this;
}
//引用返回,复用尾插
wxt::string& wxt::string::operator+=(const char* str)
{
assert(str);
append(str);
return *this;
}
//插入,思路:
//先判断是否扩容,挪动数据,插入数据
void wxt::string::insert(size_t pos, char ch)
{
assert(pos <= _size);//判断插入位置是否合法
if (_capacity == _size)//判断是否需要扩容
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
size_t end = _size + 1;
while (end > pos)//挪动数据,从pos位置向后挪动一位
//同时会挪动'\0'!
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;//插入数据
_size++;//更新对象大小
}
//void wxt::string::insert(size_t pos, const char* str)
//{
// assert(pos <= _size);
// assert(str);
// size_t len = strlen(str);
// if (_size + len > _capacity)
// {
// reserve(_size + len);
// }
// _size += len;
// size_t end = _capacity;
// while (end > pos + len)
// {
// _str[end - 1] = _str[end - len - 1];
// --end;
// }
// strcpy(_str + pos, str);
//}
//void wxt::string::erase(size_t pos, size_t len = npos)
//{
// assert(pos <= _size);
// if (len < _capacity - _size)
// {
// while (len > 0)
// {
//
// }
// }
//}
//直接比较对象中的字符串,根据返回值判断比较结果
bool wxt::string::operator<(const wxt::string& s) const
{
return strcmp(_str, s._str) < 0;
}
bool wxt::string::operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;
}
bool wxt::string::operator<=(const string& s) const
{
return (*this < s) || (*this == s);
}
bool wxt::string::operator>(const string& s) const
{
return !(*this < s);
}
bool wxt::string::operator>=(const string& s) const
{
return (*this > s) || (*this == s);
}
bool wxt::string::operator!=(const string& s) const
{
return !(*this == s);
}
void wxt::string::clear()
{
_size = 0;
_str[0] = '\0';
}
const char* wxt::string::str() const
{
return _str;
}
ostream& wxt::operator<<(ostream& out, const wxt::string& s)
{
for (size_t i = 0; i < s.size(); i++)//循环打印
{
out << s[i];
}
return out;//返回istream指针
}
istream& wxt::operator>>(istream& in, string& s)
{
s.clear();//清空对象
char ch;
ch = in.get();//使用get方法获取一个流字符
while (ch != ' ' && ch != '\n')//当获取的字符不为空格和换行时
{
s += ch;//字符尾插到对象
ch = in.get();//获取下一个字符
}
return in;//返回istream指针
}
//针对const对象的迭代器。
wxt::string::iterator wxt::string::begin() const
{
return _str;
}
wxt::string::iterator wxt::string::end() const
{
return _str + _size;
}
//————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————
//插入字符串
void wxt::string::insert(size_t pos, const char* str)
{
assert(pos <= _size);//断言下标是否合法
size_t len = strlen(str);//求要插入的字符串的长度
if (_size + len > _capacity)//超过空间则扩容
{
reserve(_size + len);
}
size_t end = _size + 1;//从后向前挪动,标记最后要操作的位置
while (end > pos)//挪动数据
{
_str[end + len - 1] = _str[end - 1];
--end;
}
_size += len;//更新对象大小
strncpy(_str + pos, str, len);//使用strncpy不使用strcpy,防止将字符串的'\0'也拷贝过来
}
//void wxt::string::insert(size_t pos, const char* str)
//{
// assert(pos <= _size);//断言下标是否合法
// size_t len = strlen(str);//求要插入的字符串的长度
// if (_size + len > _capacity)//超过空间则扩容
// {
// reserve(_size + len);
// }
// size_t end = _size + len + 1;//从后向前挪动,标记最后要操作的位置
// while (end > _size)//挪动数据
// {
// _str[end - 1] = _str[end - 1 - len];
// --end;
// }
// _size += len;//更新对象大小
// strncpy(_str + pos, str, len);//使用strncpy不使用strcpy,防止将字符串的'\0'也拷贝过来
//}
//删除字符或字符串
void wxt::string::erase(size_t pos, size_t len)
{
assert(pos < _size);//判断下标是否合法,由于删除的是数据,所以合法的下标不能是_size
if (len == std::string::npos || len + pos >= _capacity)
{//处理全删和默认不传值的情况,不传值默认全删
_str[pos] = '\0';//直接将指定位置设置为'\0'
_size = pos;//更新对象大小,pos处为'\0',也就是_size的值
}
else
{//处理删除部分数据的情况
size_t begin = pos + len;//设置要挪动数据的起始位置
while (begin <= _size)//循环挪动数据,结束条件为起始位置和末尾数据所在位置重叠
{
_str[begin - len] = _str[begin];
++begin;
}
_size -= len;//更新对象大小
}
}
//分两种情况,n小于_size和n大于_size
//小于则直接设置‘\0’,大于先扩容,然后填充字符
void wxt::string::rsize(size_t n, char ch)
{
if (n <= _size)
{
_str[n] = '\0';
_size = n;
}
else
{
reserve(n);//扩容,如果n小于_capacity,不扩容。
while (_size < n)//如果n大于_size,说明需要插入指定字符
{//循环,知道n等于_size再终止
_str[_size] = ch;
++_size;
}
_str[_size] = '\0';//将_size设置为\0
}
}
//find查找字符
//size_t wxt::string::find(char ch, size_t pos) const
//{
// //不需要使用assert断言下标是否合法
// //因为,下标不合法和没有查到,都可以返回npos
// while (pos < _size)
// {
// if (_str[pos] == ch)
// {
// return pos;
// }
// ++pos;
// }
// return std::string::npos;
//}
size_t wxt::string::find(char ch, size_t pos) const
{
//不需要使用assert断言下标是否合法
//因为,下标不合法和没有查到,都可以返回npos
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return std::string::npos;
}
//find查找字符串
size_t wxt::string::find(const char* str, size_t pos) const
{
//无需assert断言,下标不合法返回npos即可
const char* p = strstr(_str + pos, str);
if (p)
{
return p - _str;//指针减去指针,得到相差的元素个数
}
return std::string::npos;
}
wxt::string wxt::string::substr(size_t pos, size_t len) const
{
wxt::string s;//创建一个用于返回的对象
size_t end = pos + len;//设置结束位置
if (len == std::string::npos || len + pos >= _size)
{
len = _size - pos;//求出要截取的长度,用于之后开辟空间
end = _size;//如果是全取,更新结束位置为最后一个元素的后一个位置,也就是'\0'处
}
s.reserve(len);//一次开辟好空间
for (size_t i = pos; i < end; i++)
{
s += _str[i];//将截取的子串,一个字符一个字符的添加到要返回的对象中
}
return s;//返回子串对象
}