目录
一、定义my_string类
此处标明:以下成员函数都是写在my_list类内部的
#include<iostream>
#include<assert.h>
#include<string>
using namespace std;
class my_string{
private:
char* _ptr; //字符串
size_t _size; //有效字符个数
size_t _capacity; //容量
static size_t npos; //size_t类型中最大数(表示-1)
public:
//交换
void Swap(my_string& str)
{
swap(_ptr, str._ptr);
swap(_size, str._size);
swap(_capacity, str._capacity);
}
//成员函数及其他功能在下面实现
};
size_t my_string::npos = -1;
二、成员函数
(一)构造函数
对象中需要额外的资源,则显示写出构造函数;
构造函数参数缺省一个空字符串,写成一个默认构造;
my_string(char* str = "")
{
_size = strlen(str);
_capacity = _size;
_ptr = new char[_size + 1]; //开一个_size+1的空间,给'\0'留一个位置
strcpy(_ptr, str);
}
构造函数中申请一个_size+1的空间,用来将字符串拷贝到对象中,留出一个 \0 的位置;
(二)析构函数
对象中有额外的资源开销,需要显示写出析构函数;
~my_string()
{
//对象资源不为nullptr,则进行销毁
if (_ptr)
{
delete[] _ptr;
_ptr = nullptr;
_size = 0;
_capacity = 0;
}
}
(三)拷贝构造
1、传统写法
my_string(const my_string& str)
:_ptr(new char[str._capacity + 1]) //此处开了str._capacity + 1的空间,是因为_capacity是最大有效容量,不包括'\0'
, _size(str._size)
, _capacity(str._capacity)
{
strcpy(_ptr, str._ptr);
}
2、简便写法
简便写法就是先将对象本身初始化为空,再通过构造函数创建一个临时的对象,与对象本身交换以达到拷贝的效果;
my_string(const my_string& str)
:_ptr(nullptr) //此处让_ptr = nullptr是为了销毁临时对象时不会造成销毁错误
, _size(0)
, _capacity(0)
{
//创建一个临时对象
my_string tmp(str._ptr);
//与临时对象进行交换
Swap(tmp);
}
_ptr(nullptr) 这个得目的是临时对象在调用析构的时候也可以正常运行,如果不初始化为 nullptr 的话,则_ptr会指向内存中的地址,那么再析构销毁时就会出现问题;
(四)赋值运算符重载
对象有资源开销,则显示写出赋值运算符重载函数,完成资源的深拷贝;
1、传统写法
my_string& operator=(const my_string& str)
{
//不是自己给自己赋值,则进入判断
if (this != &str)
{
//开空间
char* tmp = new char[str._capacity + 1];
//释放原有空间
delete[] _ptr;
//拷贝
strcpy(tmp, str._ptr);
//改变指针指向,完成深拷贝
_ptr = tmp;
_size = str._size;
_capacity = str._capacity;
}
return *this;
}
2、简便写法
my_string& operator=(my_string str)
{
Swap(str);
return *this;
}
这种写法的深拷贝在传参的时候就已经进行了,参数接收时用了一个拷贝构造拷贝了要赋值的对象,再将其与被赋值对象进行资源交换,则完成了深拷贝赋值;
三、迭代器
迭代器底层上其实就是指针,不过是将指针起了一个别名叫iterator,所以实现时也就是用指针实现的;
begin其实就是获取第一个元素的地址;end是获取最后一个元素的下一个位置的地址;
需要注意的是const迭代器必须用const来修饰函数;
//迭代器iterator
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _ptr;
}
iterator end()
{
return _ptr + _size; //指向最后一个索引的下一个位置
}
const_iterator begin() const
{
return _ptr;
}
const_iterator end() const
{
return _ptr + _size;
}
const_iterator cbegin() const
{
return _ptr;
}
const_iterator cend() const
{
return _ptr + _size;
}
四、Capacity
(一)获取string容量
1、获取有效元素个数:size()
size_t size()
{
return _size;
}
2、获取最大有效元素容量:capacity()
size_t capacity()
{
return _capacity;
}
3、判空
bool empty() const
{
if (_size == 0)
return true;
return false;
}
(二)改变容量
1、扩容:reserve()
当容量不够时需要扩容,需要重新开辟一块更大的空间;
void reserve(size_t n)
{
if (n >= _capacity)
{
//开空间
char* tmp = new char[_capacity + 1];
//拷贝
strcpy(tmp, _ptr);
//删除原有空间
delete[] _ptr;
//改变指向
_ptr = tmp;
_capacity = n;
}
}
2、改变有效元素的个数:resize()
改变容量resize通常有以下几方面的情况:
(1)n < _size:缩容
(2)_size < n < _capacity:增容 + 赋值
(3)n > _capacity:扩容 + 增容 + 赋值
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
if (n > _capacity)
{
reserve(n);
}
memset(_ptr + _size, ch, (n - _size) * sizeof(char));
}
_size = n;
_ptr[_size] = '\0';
}
五、元素访问
(一)[]运算符重载
char& operator[](int i)
{
//判断位置是否越界
assert(i < _size);
return _ptr[i];
}
const char& operator[](int i) const
{
assert(i < _size);
return _ptr[i];
}
六、string修改
(一)插入insert
1、插入单个字符
在pos位置的前面插入一个字符;
插入时,将待插入位置后面的字符从后向前依次向后移动;
my_string& insert(size_t pos, char ch)
{
//判断访问是否越界
assert(pos <= _size);
//判断容器是否满了
if (_size == _capacity)
{
size_t new_capacity = _capacity == 0 ? 15 : 2 * _capacity;
reserve(new_capacity);
}
size_t end = _size;
//从后向前移动元素
while (end > pos)
{
_ptr[end] = _ptr[end - 1];
end--;
}
_ptr[pos] = ch;
_ptr[++_size] = '\0'; //最后需要在_size位置补上‘\0’
return *this;
}
2、插入字符串
在pos位置前面插入一个字符串;
插入时,将待插入位置后面的字符从后向前依次向后移动;
my_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 > pos + len - 1)
{
_ptr[end] = _ptr[end - len];
end--;
}
memcpy(_ptr + pos, str, len);
_size += len;
_ptr[_size] = '\0';
return *this;
}
(二)删除erase
将pos位置向后n个字符删除,pos缺省为0,len缺省为最大值npos(也就是将从pos位置到字符串结尾全部删除);
删除时,将删除的元素后面的元素从前向后向前移动;
my_string& erase(size_t pos = 0, size_t len = npos)
{
//判断边界
assert(pos < _size);
//判断是否是无效删除位置
if ((pos + len >= _size) || (len == npos))
{
_size = pos;
_ptr[_size] = '\0';
return *this;
}
size_t start = pos + len;
//从前向后移动位置
while (start < _size)
{
_ptr[start - len] = _ptr[start];
start++;
}
_size -= len;
_ptr[_size] = '\0';
return *this;
}
(三)尾插push_back
void push_back(char ch)
{
insert(_size, ch);
}
(四)尾删pop_back
void pop_back()
{
erase(_size - 1);
}
(五)追加插入append
void append(const char* str)
{
insert(_size, str);
}
(六)+=运算符重载
+=运算会改变原对象的内容,所以返回引用
my_string& operator+=(const my_string& str)
{
append(str._ptr);
return *this;
}
七、关系运算符重载
//<运算符重载
bool operator<(const my_string& str)
{
if (strcmp(_ptr, str._ptr) < 0)
return true;
return false;
}
//<=运算符重载
bool operator<=(const my_string& str)
{
if (*this > str)
return false;
return true;
}
//>运算符重载
bool operator>(const my_string& str)
{
if (strcmp(_ptr, str._ptr) > 0)
return true;
return false;
}
//>=运算符重载
bool operator>=(const my_string& str)
{
if (*this < str)
return false;
return true;
}
//==运算符重载
bool operator==(const my_string& str)
{
if (strcmp(_ptr, str._ptr) == 0)
return true;
return false;
}
//!=运算符重载
bool operator!=(const my_string& str)
{
if (*this == str)
return false;
return true;
}
八、查找
1、查找一个字符
从pos位置开始查找一个字符,pos缺省值为0;
因为查找时不能修改字符串,所以需要用const来修饰该函数;
返回c在string中第一次出现的位置
// 返回c在string中第一次出现的位置
size_t find(char ch, size_t pos = 0) const
{
size_t count = 0;
while (count < _size)
{
if (*(_ptr + pos + count) == ch)
return count;
count++;
}
return count;
}
2、查找一个字符串
从pos位置开始查找一个字符串,pos缺省值为0;
因为查找时不能修改字符串,所以需要用const来修饰该函数;
返回子串s在string中第一次出现的位置
// 返回子串s在string中第一次出现的位置
size_t find(const char* str, size_t pos = 0) const
{
size_t len = strlen(str);
size_t count = 0;
while (count < _size - len + 1)
{
size_t i = pos + count;
while (i < count + len)
{
size_t j = 0;
if (_ptr[i] != str[j])
break;
i++;
j++;
}
if (i == count + len)
return count;
count++;
}
return count;
}
九、输入输出运算符重载(不是成员函数)
输入输出运算符重载函数不是成员函数,不要写到my_string类中;
(一)输入运算符重载
istream& operator>>(istream& _cin, my_string& str)
{
char ch;
while ((ch = getchar()) != EOF)
{
if (ch == '\n')
return _cin;
str += ch;
}
return _cin;
}
(二)输出运算符重载
ostream& operator<<(ostream& _cout, const my_string& str)
{
for (const auto& ch : str)
{
_cout << ch;
}
return _cout;
}