文章目录
string类的模拟实现我们定义三个成员变量
class string
{
public:
//...
private:
char* _str;
size_t _size;
size_t _capacity;
};
下面对其中的成员函数进行依次的实现
一、string类的构造,拷贝构造,赋值重载以及析构
1.构造函数
构造函数分为无参 和带参这两种
无参构造函数默认构造空字符串"",所以我们只需要写一个带参的并给一个缺省值即可
string::string(const char* str)
:_size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
注意:这里的_str开空间时要加1,是给\0预留位置
2.拷贝构造
对于string类型来说,如果不写拷贝构造会导致浅拷贝问题(只完成值拷贝)
所以我们需要进行深拷贝
这里先介绍一种传统写法,也是比较简单的写法
string::string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
3.swap问题
在之前学习string的使用时,我们会发现明明std中就有swap,为什么string还要再实现一个?


这是因为第一个swap的交换代价比较大,它会进行深拷贝,造成空间损耗,所以我们在自己实现swap时要考虑到这一点,可以采用交换两者指针的方式:
void string::swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
4.赋值重载
默认生成的赋值重载也会导致浅拷贝,所以我们需要实现深拷贝
这里我们也用传统写法,也是比较简单的写法
string& string::operator=(const string& s)
{
char* tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
return *this;
}
其实还有一种现代写法,是复用上面的拷贝构造,感兴趣的小伙伴可以自行先去了解哦
5.析构函数
析构函数比较简单,请看下面的代码:
string::~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
下面我们来看几个常用的接口实现
二.常用接口
1.c_str
const char* string::c_str() const
{
return _str;
}
2.operator[]
char& string::operator[](size_t pos)//普通对象:可读可写
{
assert(pos < _size);
return _str[pos];
}
3.迭代器和范围for
迭代器
typedef char* iterator;
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
//遍历
string::iterator it1 = s1.begin();
while (it1 != s1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
范围for(底层还是迭代器)
typedef char* iterator;
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
//遍历
for (auto e: s1)
{
cout << e << " ";
}
cout << endl;
打印结果如下:

4.size
size_t size() const
{
return _size;
}
三.插入
reserve
开辟新的空间,然后进行拷贝,对旧空间进行释放
void string::reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
push_back
尾插一个字符,我们需要考虑扩容问题
同时,尾插之后'\0'要重新处理
void string::push_back(char ch)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_str[_size + 1] = '\0';
++_size;
}
append
尾插字符串,这里我们需要计算,然后决定开多少空间(直接开2倍可能不够用)
void string::append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str+_size, str);
_size += len;
}
+=
这个比较简单,我们可以复用上面的分为+=字符和+=字符串push_back和append
string& string::operator+=(char ch)//字符
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)//字符串
{
append(str);
return *this;
}
insert
insert的代码,我们要注意画图,尤其是关于边界的一些条件的处理,我们要小心
void string::insert(size_t pos, char ch)//处理字符
{
assert(pos <= _size);
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size + 1;
while (end > pos)//这里的边界要控制清楚!
{
_str[end] = _str[end - 1];//右移
--end;
}
_str[pos] = ch;
++_size;
}
void 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;
while (end > pos + len - 1)//这里的边界要控制清楚!
{
_str[end] = _str[end - len];//右移
--end;
}
memcpy(_str + pos, str, len);
_size += len;
}
四.删除
说到erase,自然要跟npos联系起来,npos是string类的静态成员变量
我们可以采用定义和声明分离的方法:
const static size_t npos;//写在string.h中
const size_t string::npos = -1;//写在string.cpp中
下面来实现这个删除功能:
void string::erase(size_t pos, size_t len)
{
assert(pos < _size);
// len大于前面字符个数时,有多少删多少
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
五.查找
从pos处开始查找字符或者字符串,找到返回下标值,没找到则返回npos
size_t string::find(char ch, size_t pos)//字符
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t string::find(const char* sub, size_t pos)//字符串
{
char* p = strstr(_str + pos, sub);//调用现成的strstr来实现
return p - _str;
}
六.流插入<<和流提取>>
流插入(输出)和流提取(输入)我们之前在日期类的时候也实现过了,我们知道它不适合写成 成员函数,之前我们用的是友元,这样才能访问私有,但这里我们可以不访问私有,挨个挨个遍历,所以可以这么写:
流插入<<
ostream& operator<< (ostream& os, const string& str)//流插入(输出)
{
for (size_t i = 0; i < str.size(); i++)
{
os << str[i];
}
return os;
}
流提取>>
istream& operator>> (istream& is, string& str)//流提取(输入)
{
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = is.get();
}
return is;
}
但是这样还不够,我们会发现之前的数据它还留着:

所以我们需要再写个clear()来清理数据:
void string::clear()
{
_str[0] = '\0';
_size = 0;
}
istream& operator>> (istream& is, string& str)//流提取(输入)
{
str.clear();
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = is.get();
}
return is;
}

七.参考代码
这里模拟实现string类用了string.h , string.cpp 和 test.cpp 三个文件来实现
这里给出string的各个函数定义和实现的代码
string.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <iostream>
#include <assert.h>
#include <string>
using namespace std;
namespace bit
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
string(const char* str = "");
~string();
const char* c_str() const;
size_t size() const;
char& operator[](size_t pos);
const char& operator[](size_t pos) const;
void push_back(char ch);
void append(const char* str);
void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);
void reserve(size_t n);
string& operator+=(char ch);
string& operator+=(const char* str);
void erase(size_t pos = 0, size_t len = npos);
string(const string& s);
string& operator=(const string& s);
size_t find(char ch, size_t pos = 0);
size_t find(const char* sub, size_t pos = 0);
void swap(string& s);
string substr(size_t pos = 0, size_t len = npos);
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();
private:
char* _str;
size_t _size;
size_t _capacity;
const static size_t npos;
};
istream& operator>> (istream& is, string& str);
ostream& operator<< (ostream& os, const string& str);
}
string.cpp
#include "string.h"
namespace bit
{
const size_t string::npos = -1;
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
string::const_iterator string::begin() const
{
return _str;
}
string::const_iterator string::end() const
{
return _str + _size;
}
string::string(const char* str)
:_size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
string::string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
string::~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
const char* string::c_str() const
{
return _str;
}
size_t string::size() const
{
return _size;
}
string& string::operator=(const string& s)
{
if (this != &s)
{
char* tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
char& string::operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& string::operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
void string::reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];//右移
--end;
}
_str[pos] = ch;
++_size;
}
void 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;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];//右移
--end;
}
memcpy(_str + pos, str, len);
_size += len;
}
void string::push_back(char ch)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_str[_size + 1] = '\0';
++_size;
}
void string::append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str+_size, str);
_size += len;
}
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
void string::erase(size_t pos, size_t len)
{
assert(pos < _size);
// len大于前面字符个数时,有多少删多少
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
size_t string::find(char ch, size_t pos)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t string::find(const char* sub, size_t pos)
{
char* p = strstr(_str + pos, sub);
return p - _str;
}
void string::swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
string string::substr(size_t pos, size_t len)
{
// len大于后面剩余字符,有多少取多少
if (len > _size - pos)
{
string sub(_str + pos);//直接构造子串返回
return sub;
}
//len <= _size - pos
else
{
string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;
}
}
bool string::operator<(const string& s) const
{
return strcmp(_str, s._str) < 0;
}
bool string::operator>(const string& s) const
{
return !(*this <= s);
}
bool string::operator<=(const string& s) const
{
return *this < s || *this == s;
}
bool string::operator>=(const string& s) const
{
return !(*this < s);
}
bool string::operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;
}
bool string::operator!=(const string& s) const
{
return !(*this == s);
}
void string::clear()
{
_str[0] = '\0';
_size = 0;
}
istream& operator>> (istream& is, string& str)
{
str.clear();
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = is.get();
}
return is;
}
ostream& operator<< (ostream& os, const string& str)
{
for (size_t i = 0; i < str.size(); i++)
{
os << str[i];
}
return os;
}
}