string类的简单模拟实现
一、前言
C++ 为了更安全、简单的使用字符串,对 C语言 中的字符串和相关函数进行封装,实现了 string 类。接下来将会实现 string 与一部分常用的函数。
参考:legacy.cplusplus.com网站的 string
具体实现:
- string部分构造函数、析构函数、赋值运算符重载。
- Iterator 中的 begin、end。
- Capacity 中的 size、resize、capacity、reserve、clear、empty。
- Element access 中的 operator[]。
- Modifiers 中的 operator+=、append、push_back、insert、erase、swap、pop_back 。
- String operations 中的 c_str、find。
- Non-member function overload 中的 relational operators、operator>>、operator<<。
二、string 的成员变量
在 string.h 中:
namespace my
{
class string
{
public:
static const size_t npos = -1;
private:
char* _str;
size_t _capacity;
size_t _size;
};
}
- npos 表示的是 string 的结束位子,详细可参考:C++中string::npos的一些用法总结
- _str 字符指针用来存储 在堆上开辟的 字符串数据的 地址。
- _capacity 表示容器空间大小,因为 C语言 字符串末尾隐藏了一个 ’ \0 ',则真实存储字符的容量为 _capacity - 1。
- _size 表示当前字符串大小(不含 ’ \0 ')。
三、string 的主要部分实现
考虑到代码复用,实现并不按照上述排列顺序,且只列举一部分重要的功能。
reserve
C++ 对 string reserve函数规定:
- 当 原空间大小 小于 需求大小 时, 对原空间扩容到需求大小或其以上。
- 当 原空间 大于或等于 需求 时,非必要的缩减空间调整。
这里实现的空间增长为严格的 2 倍扩容,对于 序号 2 的情况采取直接忽视。
在 string.cpp 中:
void my::string::reserve(size_t n)
{
if (n + 1 <= _capacity) // 考虑末尾 '\0'
{
return;
}
size_t newCapacity = _capacity == 0 ? MY_STRING_INIT_CAPACITY : _capacity * 2;
// n 仍然大于newCapacity时继续扩容
// newCapacity <= n 等价 newCapacity < n + 1
while (newCapacity <= n)
{
newCapacity <<= 1;
}
char* str1 = nullptr; // str1存储原空间地址
std::swap(str1, _str);
_str = new char[newCapacity]; // _str 新开空间后拷贝
// _size >= _capacity 只出现在初始化时(这时 _size == _capacity == 0)
// 以防止超出 _capacity 的非法内存访问
memcpy(_str, str1, sizeof(char) * (_size >= _capacity ? _capacity : _size + 1));
_capacity = newCapacity;
delete[] str1;
}
Iterator
这里迭代器只对原生 char* 进行封装。
参考:std::string::begin
参考:std::string::end
这里只实现部分。
在 my::string 中:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
operator[]
在 my::string 类中
char& operator[](size_t index)
{
return *(_str + index);
}
const char& operator[](size_t index) const
{
return *(_str + index);
}
insert
这里只实现两个。
在 string.cpp 中:
// 在 pos 位置上插入 字符ch / 字符串str,并返回该string
my::string& my::string::insert(size_t pos, char ch)
{
assert(pos <= _size);
reserve(++_size);
// 将 pos 原位置与之后字符向后移动一步,再用 ch 覆盖 pos 当前原字符
for (size_t end = _size; end >= pos + 1; --end)
{
_str[end] = _str[end - 1];
}
_str[pos] = ch;
return *this;
}
my::string& my::string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len != 0)
{
reserve(_size += len);
// 将 pos 原位置与之后字符向后移动len步,再用 str 覆盖 pos 和之后原字符串
for (size_t end = _size; end >= pos + len; --end)
{
_str[end] = _str[end - len];
}
strncpy(_str + pos, str, len);
}
return *this;
}
erase
这里只实现一个。
在 string.cpp 中:
// 删除 pos ~ pos + len 位置上的元素,并返回该 string
my::string& my::string::erase(size_t pos, size_t len)
{
assert(pos < _size);
if (len > _size) // len 默认值为 npos, 此时不能加,会溢出
{
len = _size;
}
// 若后面还有数据需移动
for (size_t end = pos; end < _size - len; ++end)
{
_str[end] = _str[end + len];
}
_size = (_size - pos <= len ? pos : _size - len);
_str[_size] = '\0'; // '\0' 需单独处理
return *this;
}
find
这里只实现两个。
在 string.cpp 中:
// 返回 ch 在 string 中第一次出现的位置
size_t my::string::find(char ch, size_t pos) const
{
assert(pos <= _size);
size_t index = pos;
while (index < _size)
{
if (_str[index] == ch)
{
break;
}
++index;
}
return index == _size ? npos : index;
}
// 返回子串 str1 在 string 中第一次出现的位置
size_t my::string::find(const char* str1, size_t pos) const
{
size_t len1 = strlen(str1);
assert(pos + len1 <= _size);
size_t index = pos;
while (index + len1 <= _size)
{
if (strncmp(_str + index, str1, len1) == 0)
{
break;
}
++index;
}
return index + len1 <= _size ? index : npos;
}
resize
在 my::string 中:
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
reserve(n);
for (int i = _size + 1; i < n; ++i)
{
_str[i] = ch;
}
}
_str[_size = n] = '\0';
}
构造、析构、赋值重载
参考:std::string::string
参考:std::string::~string
参考:std::string::operator=
在 my::string 中:
string(const char* str1 = "")
:_str(nullptr)
,_size(0)
,_capacity(0)
{
size_t len1 = strlen(str1);
reserve(len1);
memcpy(_str, str1, sizeof(char) * (len1 + 1));
_size = len1;
}
string(const string& str1)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
reserve(str1.capacity());
memcpy(_str, str1._str, sizeof(char) * (str1._size + 1));
_size = str1._size;
}
~string()
{
if (_str != nullptr)
{
delete[] _str;
_str = nullptr;
}
_size = _capacity = 0;
}
void swap(string& str1)
{
std::swap(_str, str1._str);
std::swap(_size, str1._size);
std::swap(_capacity, str1._capacity);
}
string& operator=(string str1)
{
swap(str1);
return *this;
}
四、string 源代码
在 string.h 中:
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cassert>
#define MY_STRING_INIT_CAPACITY 4
namespace my
{
class string
{
friend std::ostream& operator<<(std::ostream& cout, const my::string& str1);
friend std::istream& operator>>(std::istream& cin, my::string& str1);
public:
typedef char* iterator;
public:
string(const char* str1 = "")
:_str(nullptr)
,_size(0)
,_capacity(0)
{
size_t len1 = strlen(str1);
reserve(len1);
memcpy(_str, str1, sizeof(char) * (len1 + 1));
_size = len1;
}
string(const string& str1)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
reserve(str1.capacity());
memcpy(_str, str1._str, sizeof(char) * (str1._size + 1));
_size = str1._size;
}
~string()
{
if (_str != nullptr)
{
delete[] _str;
_str = nullptr;
}
_size = _capacity = 0;
}
void swap(string& str1)
{
std::swap(_str, str1._str);
std::swap(_size, str1._size);
std::swap(_capacity, str1._capacity);
}
string& operator=(string str1)
{
swap(str1);
return *this;
}
//-----------------------------------------------------------
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
//-----------------------------------------------------------
void push_back(char ch)
{
insert(_size, ch);
}
void pop_back()
{
erase(_size - 1, 1);
}
string& operator+=(char ch)
{
return insert(_size, ch);
}
void append(const char* str1)
{
insert(_size, str1);
}
string& operator+=(const char* str1)
{
return insert(_size, str1);
}
void clear()
{
_str[_size = 0] = 0;
}
const char* c_str() const
{
return _str;
}
//-----------------------------------------------------------
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity - 1;
}
bool empty() const
{
return _size == 0;
}
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
reserve(n);
for (int i = _size + 1; i < n; ++i)
{
_str[i] = ch;
}
}
_str[_size = n] = '\0';
}
void reserve(size_t n);
//-----------------------------------------------------------
char& operator[](size_t index)
{
return *(_str + index);
}
const char& operator[](size_t index) const
{
return *(_str + index);
}
//-----------------------------------------------------------
bool operator<(const string& str1)
{
return strcmp(c_str(), str1.c_str()) < 0;
}
bool operator>(const string& str1)
{
return strcmp(c_str(), str1.c_str()) > 0;
}
bool operator==(const string& str1)
{
return strcmp(c_str(), str1.c_str()) == 0;
}
bool operator<=(const string& str1)
{
return !(*this > str1);
}
bool operator>=(const string& str1)
{
return !(*this < str1);
}
bool operator!=(const string& str1)
{
return !(*this == str1);
}
//-----------------------------------------------------------
size_t find(char ch, size_t pos = 0) const;
size_t find(const char* str1, size_t pos = 0) const;
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char* str);
string& erase(size_t pos = 0, size_t len = npos);
static const size_t npos = -1;
private:
char* _str;
size_t _capacity;
size_t _size;
};
}
在 string.cpp 中:
#include "string.h"
void my::string::reserve(size_t n)
{
if (n + 1 <= _capacity) // 考虑末尾 '\0'
{
return;
}
size_t newCapacity = _capacity == 0 ? MY_STRING_INIT_CAPACITY : _capacity * 2;
// n 仍然大于newCapacity时继续扩容
// newCapacity <= n 等价 newCapacity < n + 1
while (newCapacity <= n)
{
newCapacity <<= 1;
}
char* str1 = nullptr; // str1存储原空间地址
std::swap(str1, _str);
_str = new char[newCapacity]; // _str 新开空间后拷贝
// _size >= _capacity 只出现在初始化时(这时 _size == _capacity == 0)
// 以防止超出 _capacity 的非法内存访问
memcpy(_str, str1, sizeof(char) * (_size >= _capacity ? _capacity : _size + 1));
_capacity = newCapacity;
delete[] str1;
}
// 返回c在string中第一次出现的位置
size_t my::string::find(char ch, size_t pos) const
{
assert(pos <= _size);
size_t index = pos;
while (index < _size)
{
if (_str[index] == ch)
{
break;
}
++index;
}
return index == _size ? npos : index;
}
// 返回子串str1在string中第一次出现的位置
size_t my::string::find(const char* str1, size_t pos) const
{
size_t len1 = strlen(str1);
assert(pos + len1 <= _size);
size_t index = pos;
while (index + len1 <= _size)
{
if (strncmp(_str + index, str1, len1) == 0)
{
break;
}
++index;
}
return index + len1 <= _size ? index : npos;
}
// 在 pos 位置上插入 字符ch / 字符串str,并返回该string
my::string& my::string::insert(size_t pos, char ch)
{
assert(pos <= _size);
reserve(++_size);
// 将 pos 原位置与之后字符向后移动一步,再用 ch 覆盖 pos 当前原字符
for (size_t end = _size; end >= pos + 1; --end)
{
_str[end] = _str[end - 1];
}
_str[pos] = ch;
return *this;
}
my::string& my::string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len != 0)
{
reserve(_size += len);
// 将 pos 原位置与之后字符向后移动len步,再用 str 覆盖 pos 和之后原字符串
for (size_t end = _size; end >= pos + len; --end)
{
_str[end] = _str[end - len];
}
strncpy(_str + pos, str, len);
}
return *this;
}
// 删除 pos ~ pos + len 位置上的元素,并返回该 string
my::string& my::string::erase(size_t pos, size_t len)
{
assert(pos < _size);
if (len > _size) // len 默认值为 npos, 此时不能加,会溢出
{
len = _size;
}
// 若后面还有数据需移动
for (size_t end = pos; end < _size - len; ++end)
{
_str[end] = _str[end + len];
}
_size = (_size - pos <= len ? pos : _size - len);
_str[_size] = '\0'; // '\0' 需单独处理
return *this;
}
std::ostream& my::operator<<(std::ostream& out, const my::string& str1)
{
out << str1.c_str();
return out;
}
std::istream& my::operator>>(std::istream& in, my::string& str1)
{
str1.clear();
// 缓冲
char buf[100] = { 0 };
char ch = in.get();
int i = 0;
while (ch != ' ' && ch != '\n')
{
buf[i++] = ch;
if (i >= 99)
{
str1 += buf;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buf[i] = '\0';
str1 += buf;
}
return in;
}