文章目录
前言
模拟实现c++中string类中的一些常用接口函数接口。
一、string类的成员变量和默认成员函数
1、成员变量
char* _str; //储存字符串
size_t _capacity; //容量大小
size_t _size; //实际字符串大小
const static size_t npos; //string中默认成员变量,值为 无符号的-1
在类外初始化 npos
const size_t String::npos = -1;
2、默认成员函数
(1)、构造函数
c++库中有多个构造函数,这里只实现最常用的无参构造和用一个字符串构造。
//声明
String(const char* str = ""); //给一个缺省值,当不传参时就可以当作无参构造
//定义
String::String(const char* str)
{
//初始化
_size = strlen(str);
_capacity = _size;
_str = new char[_size + 1];//多给一个空间储存'\0'
//将str拷贝到类中的_str中
strcpy(_str, str);
}
(2)析构函数
我们这里就是对申请的空间进行使用,和对变量置空
//声明
~String();
//定义
String::~String()
{
//置0
_size = _capacity = 0;
//释放并置空
delete[]_str;
_str = nullptr;
}
(3)拷贝函数
因为有空间是在堆上申请的,所以这里需要用深拷贝进行拷贝。
//声明
String(const String& s);
//定义
String::String(const String& s): _str(nullptr)
{
//对长度和容量直接复制
_size = s._size;
_capacity = s._capacity;
//先构造一个新类作为临时变量,在对它们的_str的指向进行交换,此时就交换完成了,
//当出了函数作用域strTmp进销毁了
String strTmp(s._str);
std::swap(_str, strTmp._str);
}
(4)赋值运算符重载
和拷贝函数类似,需要使用深拷贝进行赋值。
//声明
String& operator=(String s);
//定义
String& String::operator=(String s)
{
//自己给自己赋值直接结束即可
if(s._str == _str)
return *this;
//大小和容量进行复制
_size = s._size;
_capacity = s._capacity;
//构造一个临时类
String strTmp(s._str);
//指向进行交换
std::swap(_str, strTmp._str);
return *this;
}
二、常用的接口
1、size
字符串长度,直接放回类中的_size
即可。
//声明
size_t size()const;
//定义
size_t String::size()const
{
return _size;
}
2、capacity
容量,直接返回类中的_capacity
即可。
//声明
size_t capacity()const;
//定义
size_t String::capacity()const
{
return _capacity;
}
3、c_str
c语言风格的字符串,直接返回类中的_str
即可。
//声明
const char* c_str()const
//定义
const char* String::c_str()const
{
return _str;
}
4、empty
字符串是否为空,通过大小进行判断
//声明
bool empty()const;
//定义
bool String::empty()const
{
return _size == 0;
}
5、clear
清空,使_size = 0
和改变‘\0’
的位置即可。
//声明
void clear();
//定义
void String::clear()
{
_str[0] = '\0';
_size = 0;
}
6、reserve
预留空间,需要预留的空间大于实际的空间就进行扩容,否则不做处理(即可放大不能缩小),预留空间不会影响实际字符串的大小即不会影响_size
。
//声明
void reserve(size_t newCapacity);
//定义
void String::reserve(size_t newCapacity)
{
//实际空间小于需要预留的空间
if (_capacity < newCapacity)
{
//重新申请一块空间
char* tmp = new char[newCapacity + 1];
//将原来的内容拷贝到新空间里
strcpy(tmp, _str);
//释放旧空间
delete[]_str;
//重新指向和改变容量
_str = tmp;
_capacity = newCapacity;
}
}
7、resize
改变其大小并用字符c(如果不传默认使用'\0'
)进行填充,可变大也可以变小(截取),可能会影响_capacity
。
//声明
void resize(size_t newSize, char c = ‘\0’);
//定义
void String::resize(size_t newSize, char c)
{
if (newSize > _size)
{
// 如果newSize大于底层空间大小,则需要重新开辟空间
if (newSize > _capacity)
{
reserve(newSize);
}
memset(_str + _size, c, newSize - _size);
}
_size = newSize;
_str[newSize] = '\0';
}
8、insert(字符)
在pos
位置插入一个字符c
//声明
void insert(size_t pos, char c);
//定义
void String::insert(size_t pos, char c)
{
//防止pos 大于 _size造成越界
assert( pos <= _size);
//没有预留的空间,要进行库容扩容
if (_size == _capacity)
{
size_t new_capacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(new_capacity);
}
//将pos位置后面的字符都往后移动一位,包括‘\0’
int end = _size + 1;
while (end >= (int)pos)
{
_str[end] = _str[end - 1];
end--;
}
//改变大小+将c填入pos位置
_size++;
_str[pos] = c;
}
9、insert(字符串)
在pos
位置插入一个字符串s
//声明
void insert(size_t pos, const char* str);
//定义
void String::insert(size_t pos, const char* str)
{
//防止pos 大于 _size造成越界
assert(pos <= _size);
int n = strlen(str);
//判断空间是否够用
if (_size + n > _capacity)
{
reserve(_size + n);
}
//将pos位置后的字符移动n位
for (int i = _size; i >= (int)pos ; i--)
{
_str[i + n] = _str[i];
}
//在字符串从pos位置开始填入
for (int i = 0; i < n; i++)
{
_str[i + pos] = str[i];
}
//改变大小
_size += n;
}
10、push_back
尾插一个字符c,这里可以直接复用insert
。
//声明
void push_back(char c);
//定义
void String::push_back(char c)
{
insert(_size, c);
}
11、append
这里依然复用insert
。
//声明
void append(const char* str);
//定义
void String::append(const char* str)
{
insert(_size, str);
}
12、erase
删除从pos
(不传参默认为0)位置开始的len
(不传参默认为npos
)个字符,当len = npos
或者 len >= _size - pos
时就是将pos
后面的字符全部删除。
//声明
void erase(size_t pos = 0, size_t len = npos);
//定义
void String::erase(size_t pos, size_t len)
{
//保证删除位置正确
assert(pos < _size);
//情况1,将pos位置后面的字符都删除
if (len == npos || len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
//请况2,删除pos位置后的len个字符
else
{
for (size_t i = 0; i <= len; i++)
{
_str[i + pos] = _str[pos + len + i];
}
_size -= len;
}
}
13、substr
截取一部分字符,截取从pos
(不传参默认为0)位置开始的len
(不传参默认为npos
)个字符,当len = npos
或者 len >= _size - pos
时就是将pos
后面的字符全部截取。
//声明
String substr(size_t pos = 0 , size_t len = npos) const;
//定义
String String::substr(size_t pos , size_t len) const
{
//保证位置正确
assert(pos < _size);
String s;
//假设end为pos + len
size_t end = pos + len;
//如果符合下面条件就要进行修正
if (len == npos || len >= _size - pos)
{
end = _size;
}
//截取
for (size_t i = pos; i < end; i++)
{
s.push_back(_str[i]);
}
return s;
}
14、find(字符)
查找一个字符,从pos
(不传参默认为0)位置开始直接通过遍历查找,找到返回第一次出现下标,找不到返回npos
//声明
size_t find(char c, size_t pos = 0) const;
//定义
size_t String::find(char c, size_t pos) const
{
for (int i = pos; i < size(); i++)
{
if (c == _str[i])
return i;
}
return npos;
}
15、find(字符串)
查找一个字符串,从pos
(不传参默认为0)位置开始查找,找到返回第一次出现的下标,找不到返回npos
//声明
size_t find(const char* s, size_t pos = 0) const;
//定义
size_t String::find(const char* s, size_t pos) const
{
//借助strstr直接查找
char *p = strstr(_str + pos, s);
//为NULL找不到
if (p == NULL)
return npos;
//指针 - 指针 = 中间的元素个数 ,所以找到位置 - 开始位置就是下标位置
return p - _str;
}
16、swap
将两个String进行交换,我们通过交换它们的_size
、_capacity
、_str(交换指向)
,即可完成两个String的交换。
//声明
void swap(String& s);
//定义
void String::swap(String& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
三、重载运算符
1、比较运算符重载
通过写两个重载,然后复用这两个重载完成其他比较函数的重载即可。
//声明
bool operator<(const String& s);
bool operator<=(const String& s);
bool operator>(const String& s);
bool operator>=(const String& s);
bool operator==(const String& s);
bool operator!=(const String& s);
//定义
//重载 < == ,其他的复用
bool String::operator<(const String& s)
{
//利用strcmp函数
return strcmp(_str, s._str) < 0;
}
bool String::operator==(const String& s)
{
//利用strcmp函数
return strcmp(_str, s._str) == 0;;
}
//复用
bool String::operator<=(const String& s)
{
return !(*this > s);
}
bool String::operator>(const String& s)
{
return !(*this < s || *this == s);
}
bool String::operator>=(const String& s)
{
return !(*this < s);
}
bool String::operator!=(const String& s)
{
return !(*this == s);
}
2、+= 运算符重载
可以直接复用尾插的接口完成。
//声明
String& operator+=(char c);
String& operator+=(const char* s);
//定义
String& String::operator+=(char c)
{
push_back(c);
return *this;
}
String& String::operator+=(const char* str)
{
append(str);
return *this;
}
3、[ ] 运算符重载
在内部直接通过字符串(char*
)进行访问即可。
//声明
//可改
char& operator[](size_t index) ;
//const修饰 ,不可改
const char& operator[](size_t index) const;
//定义
char& String::operator[](size_t index)
{
assert(index < _size);
return _str[index];
}
const char& String::operator[](size_t index)const
{
assert( index < _size);
return _str[index];
}
4、>> 、<<运算符重载
这两运算符需要作为全局函数进行重载,<<
运算符在内部直接对字符串进行访问即可,>>
运算符 通过一个字符一个字符输入并借助 +=
运算符进行插入到尾部,直到遇到空格或者换行结束。
//输出
ostream& operator<<(ostream& _cout, const String& s)
{
//直接访问字符串_str
for (int i = 0; i < s.size(); i++)
{
cout << s[i];
}
return _cout;
}
//输入
istream& operator>>(istream& _cin, String& s)
{
//先清空原来的字符串
s.clear();
//需要利用get这个接口,_cin是不能接受空格和换行的,get可以。
char ch = _cin.get();
//结束条件
while (ch != ' ' && ch != '\n')
{
//借助 +=
s += ch;
ch = _cin.get();
}
return _cin;
}
四、迭代器
我们要对 char*
重命名为 iterator
,const char*
重命名为const_iterator
与库中的迭代器名称保持一致。
//重命名
typedef char* iterator;
typedef const char* const_iterator;
//声明
iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end() const;
//定义
//返回第一个位置的指针
String::iterator String::begin()
{
return _str;
}
String::const_iterator String::begin()const
{
return _str;
}
//返回最后位置的下一个位置的指针
String::iterator String::end()
{
return _str + _size;
}
String::const_iterator String::end() const
{
return _str + _size;
}
五、全部代码
String.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cassert>
using namespace std;
namespace xu
{
class String
{
public:
typedef char* iterator;
typedef const char* const_iterator;
public:
//构造
String(const char* str = "");
//析构
~String();
//拷贝
String(const String& s);
//赋值
String& operator=(String s);
//重载 +=
String& operator+=(char c);
String& operator+=(const char* s);
//重载 []
char& operator[](size_t index) ;
const char& operator[](size_t index) const;
//重载 比较运算符
bool operator<(const String& s);
bool operator<=(const String& s);
bool operator>(const String& s);
bool operator>=(const String& s);
bool operator==(const String& s);
bool operator!=(const String& s);
//迭代器接口
// iterator
iterator begin();
iterator end();
//const_iterator
const_iterator begin()const;
const_iterator end() const;
//插入
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
void insert(size_t pos, char c);
void insert(size_t pos, const char* str);
//加一个字符
void push_back(char c);
//加一个字符串
void append(const char* str);
//查找
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const;
// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const;
//删除
//清空
void clear();
// 删除pos位置上的元素,并返回该元素的下一个位置
void erase(size_t pos = 0, size_t len = npos);
//常用其他接口
//交换
void swap(String& s);
//返回c语言风格的字符串
const char* c_str()const;
//长度
size_t size()const;
//容量
size_t capacity()const;
//是否为空
bool empty()const;
//预留
void reserve(size_t newCapacity);
//预留加修改为c
void resize(size_t newSize, char c = '\0');
//截取一段字符
String substr(size_t pos = 0, size_t len = npos) const;
private:
char* _str;
size_t _capacity;
size_t _size;
const static size_t npos;
};
ostream& operator<<(ostream& _cout, const String& s);
istream& operator>>(istream& _cin, String& s);
}
String.cpp
#include"String.h"
namespace xu
{
const size_t String::npos = -1;
//输出
ostream& operator<<(ostream& _cout, const String& s)
{
for (int i = 0; i < s.size(); i++)
{
cout << s[i];
}
return _cout;
}
//输入
istream& operator>>(istream& _cin, String& s)
{
s.clear();
char ch = _cin.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = _cin.get();
}
return _cin;
}
//构造
String::String(const char* str)
{
//初始化
_size = strlen(str);
_capacity = _size;
_str = new char[_size + 1];//多给一个空间储存'\0'
//将str拷贝到类中的_str中
strcpy(_str, str);
}
//析构
String::~String()
{
_size = _capacity = 0;
delete[]_str;
_str = nullptr;
}
//拷贝
String::String(const String& s): _str(nullptr)
{
_size = s._size;
_capacity = s._capacity;
String strTmp(s._str);
std::swap(_str, strTmp._str);
}
//赋值
String& String::operator=(String s)
{
//大小和容量进行复制
_size = s._size;
_capacity = s._capacity;
//构造一个临时类
String strTmp(s._str);
//指向进行交换
std::swap(_str, strTmp._str);
return *this;
}
iterator
String::iterator String::begin()
{
return _str;
}
String::iterator String::end()
{
return _str + _size;
}
const_iterator
String::const_iterator String::begin()const
{
return _str;
}
String::const_iterator String::end() const
{
return _str + _size;
}
//加一个字符
String& String::operator+=(char c)
{
push_back(c);
return *this;
}
//压入一个字符
void String::push_back(char c)
{
insert(_size, c);
}
//加一个字符串
String& String::operator+=(const char* str)
{
append(str);
return *this;
}
//加一个字符串
void String::append(const char* str)
{
insert(_size, str);
}
//是否为空
bool String::empty()const
{
return _size == 0;
}
//长度
size_t String::size()const
{
return _size;
}
//容量
size_t String::capacity()const
{
return _capacity;
}
//返回c语言风格的字符串
const char* String::c_str()const
{
return _str;
}
char& String::operator[](size_t index)
{
assert(index < _size);
return _str[index];
}
const char& String::operator[](size_t index)const
{
assert( index < _size);
return _str[index];
}
//清空
void String::clear()
{
_str[0] = '\0';
_size = 0;
}
//交换
void String::swap(String& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
//比较重载
bool String::operator<(const String& s)
{
return strcmp(_str, s._str) < 0;
}
bool String::operator==(const String& s)
{
return strcmp(_str, s._str) == 0;;
}
bool String::operator<=(const String& s)
{
return !(*this > s);
}
bool String::operator>(const String& s)
{
return !(*this < s || *this == s);
}
bool String::operator>=(const String& s)
{
return !(*this < s);
}
bool String::operator!=(const String& s)
{
return !(*this == s);
}
// 返回c在string中第一次出现的位置
size_t String::find(char c, size_t pos) const
{
for (int i = pos; i < size(); i++)
{
if (c == _str[i])
return i;
}
return npos;
}
// 返回子串s在string中第一次出现的位置
size_t String::find(const char* s, size_t pos) const
{
char *p = strstr(_str + pos, s);
if (p == NULL)
return npos;
return p - _str;
}
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
void String::insert(size_t pos, char c)
{
//防止pos 大于 _size造成越界
assert( pos <= _size);
//没有预留的空间,要进行库容扩容
if (_size == _capacity)
{
size_t new_capacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(new_capacity);
}
//将pos位置后面的字符都往后移动一位,包括‘\0’
int end = _size + 1;
while (end >= (int)pos)
{
_str[end] = _str[end - 1];
end--;
}
//改变大小+将c填入pos位置
_size++;
_str[pos] = c;
}
void String::insert(size_t pos, const char* str)
{
//防止pos 大于 _size造成越界
assert(pos <= _size);
int n = strlen(str);
//判断空间是否够用
if (_size + n > _capacity)
{
reserve(_size + n);
}
//将pos位置后的字符移动n位
for (int i = _size; i >= (int)pos ; i--)
{
_str[i + n] = _str[i];
}
//在字符串从pos位置开始填入
for (int i = 0; i < n; i++)
{
_str[i + pos] = str[i];
}
//改变大小
_size += n;
}
// 删除pos位置上的元素
void String::erase(size_t pos, size_t len)
{
//保证删除位置正确
assert(pos < _size);
//情况1,将pos位置后面的字符都删除
if (len == npos || len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
//请况2,删除pos位置后的len个字符
else
{
for (size_t i = 0; i <= len; i++)
{
_str[i + pos] = _str[pos + len + i];
}
_size -= len;
}
}
//预留
void String::reserve(size_t newCapacity)
{
//实际空间小于需要预留的空间
if (_capacity < newCapacity)
{
//重新申请一块空间
char* tmp = new char[newCapacity + 1];
//将原来的内容拷贝到新空间里
strcpy(tmp, _str);
//释放旧空间
delete[]_str;
//重新指向和改变容量
_str = tmp;
_capacity = newCapacity;
}
}
//预留加修改为c
void String::resize(size_t newSize, char c)
{
if (newSize > _size)
{
// 如果newSize大于底层空间大小,则需要重新开辟空间
if (newSize > _capacity)
{
reserve(newSize);
}
memset(_str + _size, c, newSize - _size);
}
_size = newSize;
_str[newSize] = '\0';
}
//截取字符串
String String::substr(size_t pos , size_t len) const
{
//保证位置正确
assert(pos < _size);
String s;
//假设end为pos + len
size_t end = pos + len;
//如果符合下面条件就要进行修正
if (len == npos || len >= _size - pos)
{
end = _size;
}
//截取
for (size_t i = pos; i < end; i++)
{
s.push_back(_str[i]);
}
return s;
}
}