2.1-STL库中string类的模拟实现
2.1-STL库中string类的模拟实现
对于C++的类和对象中一些零碎复杂的语法规则,我用模拟实现STL库中常见类和模板总结。
本文中模拟了string中的大部分常用接口,如尾插,中间的插入删除,reserve和resize以及运算符重载,也实现了正向迭代器。
为了能提供可直接运行的代码,接下来的内容在代码块中完成。
#pragma once
#include <iostream>
#include <cstring>
#include <cassert>
namespace my_string
{
class MyString
{
public:
// 迭代器(左闭右开)
typedef char* iterator;
typedef const char* const_iterator;
const_iterator begin() const {
return _str;
}
const_iterator end() const {
return _str + _size;
}
iterator begin() {
return _str;
}
iterator end() {
return _str + _size;
}
// 构造函数
MyString(const char* str = "") // 全缺省。
: _size(std::strlen(str))
{
_capacity = _size;
_str = new char[_capacity + 1]; // 有效字符_capacity个,额外添加一个留给\0。
std::strcpy(_str, str);
}
// 析构函数
~MyString()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
// 拷贝构造——深拷贝
MyString(const MyString& s)
: _size(s._size)
, _capacity(s._capacity)
{
_str = new char[s._capacity + 1];
std::strcpy(_str, s._str);
}
// 重载[]
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
// 赋值重载
MyString& operator=(const MyString& s)
{
if (this != &s) // 处理自赋值
{
char* temp = new char[s._capacity + 1];
std::strcpy(temp, s._str);
delete[] _str;
_str = temp;
_capacity = s._capacity;
_size = s._size;
}
return *this;
}
// 加上const,使其成为const成员函数。
const char* c_str() const
{
return _str;
}
size_t size() const
{
return _size;
}
// 有关于string比较大小的运算符重载。
bool operator>(const MyString& s) const {
return std::strcmp(_str, s._str) > 0;
}
bool operator==(const MyString& s) const {
return std::strcmp(_str, s._str) == 0;
}
bool operator<(const MyString& s) const {
return std::strcmp(_str, s._str) < 0;
}
bool operator>=(const MyString& s) const {
return std::strcmp(_str, s._str) >= 0;
}
bool operator<=(const MyString& s) const {
return std::strcmp(_str, s._str) <= 0;
}
bool operator!=(const MyString& s) const {
return std::strcmp(_str, s._str) != 0;
}
// 为了更好的实现插入,先实现reserve。
void reserve(size_t n) {
if (n > _capacity) {
char* tmp = new char[n + 1];
std::strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
// 尾插
void push_back(char ch) {
if (_size + 1 > _capacity) {
reserve(_capacity == 0 ? 3 : _capacity * 2);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
void append(const char* str) {
size_t len = std::strlen(str);
if (_size + len > _capacity) {
reserve(_size + len);
}
std::strcpy(_str + _size, str);
_size += len;
}
// 重载+=,可以直接套用append的运算符重载
MyString& operator+=(char ch) {
push_back(ch);
return *this;
}
MyString& operator+=(const char* str) {
append(str);
return *this;
}
// resize
void resize(size_t n, char ch = '\0') {
if (n < _size) {
// 删除数据
_size = n;
_str[n] = '\0';
}
else if (n >= _size) {
reserve(n);
std::memset(_str + _size, ch, n - _size);
_size = n;
_str[_size] = '\0';
}
}
// 中间的插入。
void insert(size_t pos, char ch) {
if (pos > _size) return; // 更完善的边界检查
if (_size + 1 > _capacity) reserve(_capacity == 0 ? 3 : _capacity * 2);
std::memmove(_str + pos + 1, _str + pos, _size - pos);
_str[pos] = ch;
++_size;
}
void insert(size_t pos, const char* str) {
if (pos > _size) return; // 更完善的边界检查
size_t len = std::strlen(str);
if (_size + len > _capacity) reserve(len + _capacity);
std::memmove(_str + pos + len, _str + pos, _size - pos);
std::memcpy(_str + pos, str, len);
_size += len;
}
// 中间的删除
void erase(size_t pos, size_t len = npos) {
if (pos >= _size) return; // 更完善的边界检查
if (len == npos || pos + len > _size) {
_str[pos] = '\0';
_size = pos;
}
else {
std::memmove(_str + pos, _str + pos + len, _size - pos - len);
_size -= len;
}
}
// 清空字符串
void clear() {
_size = 0;
_str[0] = '\0';
}
// 一些其他的常用接口
// 代价更低的交换
void swap(MyString& s) {
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
// 寻找
size_t find(char ch, size_t pos = 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 = 0) {
char* p = std::strstr(_str + pos, str);
if (p == nullptr) return npos;
return p - _str;
}
private:
// 成员变量习惯在开头标记下划线。
char* _str;
size_t _size;
size_t _capacity;
static const size_t npos;
};
const size_t MyString::npos = -1;
void Print(const MyString& s)
{
MyString::const_iterator it = s.begin();
while (it != s.end()) {
std::cout << *it;
it++;
}
std::cout << std::endl;
// const类型的迭代器范围for也可以自行替换,可自行去除注释验证。
// for (auto e : s) {
// std::cout << e << " ";
// }
// std::cout << std::endl;
}
// 流插入重载
std::ostream& operator<<(std::ostream& out, const MyString& s) {
for (auto e : s) {
out << e;
}
return out;
}
// 流提取重载
std::istream& operator>>(std::istream& in, MyString& s) {
s.clear(); // 清空原有的字符串内容
char ch;
while (in.get(ch) && ch != ' ' && ch != '\n') {
s += ch;
}
return in;
}
// 下面是一些测试用例。
void test_string1()
{
// 测试构造函数
MyString s1;
MyString s2("hello world!");
std::cout << s1.c_str() << std::endl;
std::cout << s2.c_str() << std::endl;
s2[0]++;
std::cout << s1.c_str() << std::endl;
std::cout << s2.c_str() << std::endl;
}
void test_string2()
{
// 验证:如果调用编译器默认拷贝构造,程序会崩溃。
MyString s1("hello world");
MyString s2(s1);
std::cout << s2.c_str() << std::endl;
MyString s3("iterator");
// 测试迭代器。
MyString::iterator it = s3.begin();
while (it != s3.end()) {
std::cout << *it << " ";
it++;
}
std::cout << std::endl;
// 范围for:是迭代器函数的傻瓜替换,故迭代器名字不变,天然可用。
for (auto ch : s3)
{
std::cout << ch << " ";
}
std::cout << std::endl;
// 测试const迭代器。
MyString s4("const iterator");
Print(s4);
}
void test_string3() {
MyString s1("hello world");
MyString s2("hello");
std::cout << (s1 < s2) << std::endl;
std::cout << (s1 > s2) << std::endl;
std::cout << (s1 <= s2) << std::endl;
}
void test_string4() {
MyString s1("hello, world");
s1.append("123456789");
Print(s1);
MyString s2("hello, ");
s2 += "1";
Print(s2);
s2 += "23456789";
Print(s2);
// 验证resize
s2.resize(6);
Print(s2);
// 验证insert
s2.insert(5, 'x');
s2.insert(5, 'x');
s2.insert(5, 'x');
s2.insert(5, 'x');
Print(s2);
s2.insert(5, "INSERT");
Print(s2);
// 验证erase
MyString s3("123456789");
s3.erase(3, 4);// 预期:12389。
Print(s3);
}
void test_string5() {
MyString s1("1234");
s1 += '\0';
s1 += "xxxxxxxx";
std::cout << s1 << std::endl;
MyString s2;
std::cin >> s2;
std::cout << s2 << std::endl;
}
}