• 【C++】string类模拟实现


    一、string类声明

    namespace yyh
    {
    	class string
    	{
    	public:
    		string(const char* str = "");// 构造
    		~string();// 析构
    		string(const string& s);// 拷贝构造
    		string& operator=(const string& s);// 赋值拷贝
    		char* c_str() const;// 返回字符串
    		size_t size() const;// 返回size
    		char& operator[](size_t pos);// []访问
    		const char& operator[](size_t pos) const;// const[]访问
    		typedef char* iterator;// 迭代器
    		iterator begin();
    		iterator end();
    		typedef const char* const_iterator;// const迭代器
    		const_iterator begin() const;
    		const_iterator end() const;
    		void push_back(const char ch);// 尾插字符
    		void append(const char* str);// 尾插字符串
    		string& operator+=(const char ch);// +=重载
    		string& operator+=(const char* str);
    		void resize(size_t n, char ch = '\0');// 初始化扩容
    		size_t find(const char ch);// 查找字符
    		size_t find(const char* str, size_t pos = 0);// 查找字符串
    		string& insert(size_t pos, const char ch);// 插入字符
    		string& insert(size_t pos, const char* str);// 插入字符串
    		string& erase(size_t pos, size_t len = npos);// 删除
    	private:
    		char* _str;
    		size_t _size;
    		size_t _capacity;// _capacity表示有效字符个数
    	public:
    		static const size_t npos = -1;
    	};
    }
    
    ostream& operator<<(ostream& out, yyh::string& s);// 输出
    istream& operator>>(istream& in, yyh::string& s);// 输入
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    二、string类接口实现

    2.1 构造函数

    yyh::string::string(const char* str)
    	: _size(strlen(str))
    	, _capacity(_size)
    {
    	_str = new char[_capacity + 1];// _capacity表示有效字符个数
    	strcpy(_str, str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们用_capacity表示有效字符的个数,那么开空间的时候就要多开一个位置(放\0

    2.2 析构函数

    yyh::string::~string()
    {
    	delete[]_str;
    	_str = nullptr;
    	_size = _capacity = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.3 拷贝构造

    yyh::string::string(const string& s)
    	: _size(strlen(s._str))
    	, _capacity(s._size)
    {
    	_str = new char[_capacity + 1];
    	strcpy(_str, s._str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.3.1 现代写法

    yyh::string::string(const string& s)
    	: _str(nullptr)
    {
    	yyh::string tmp(s._str);// 构造
    	swap(_str, tmp._str);
    	swap(_size, tmp._size);
    	swap(_capacity, tmp._capacity);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    先用s._str构造一个tmp,交换tmp和s的_str因为tmp的生命周期只在当前栈帧,栈帧销毁自动调用析构函数。
    这里要注意一定要先把_str置为空,因为s(s1)s的_str指向随机空间,交换给tmp后,调用析构函数就会程序崩溃。

    2.4 赋值重载=拷贝

    yyh::string& yyh::string::operator=(const string& s)
    {
    	if (this != &s)// 自己拷贝自己
    	{
    		char* tmp = new char[strlen(s._str) + 1];
    		strcpy(tmp, s._str);
    		delete[]_str;
    		_str = tmp;
    		_size = s._size;
    		_capacity = s._capacity;
    	}
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.4.1 现代写法

    yyh::string& yyh::string::operator=(const string& s)
    {
    	if (this != &s)
    	{
    		_str = nullptr;
    		yyh::string tmp(s);
    		swap(tmp._str, _str);
    		swap(tmp._capacity, _capacity);
    		swap(tmp._size, _size);
    	}
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.4.2 更简洁写法

    yyh::string& yyh::string::operator=(string s)
    {
    	swap(s._str, _str);
    	swap(s._size, _size);
    	swap(s._capacity, _capacity);
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意写这种写法的时候要有拷贝构造函数,s是拷贝构造出来的,生命周期只存在当前栈帧,出栈帧自动销毁。

    2.5 返回字符串

    char* yyh::string::c_str() const
    {
    	return _str;
    }
    
    • 1
    • 2
    • 3
    • 4

    2.6 返回size

    size_t yyh::string::size() const
    {
    	return _size;
    }
    
    • 1
    • 2
    • 3
    • 4

    2.7 []访问 & const[]访问

    char& yyh::string::operator[](size_t pos)
    {
    	assert(pos < _size);
    	return _str[pos];
    }
    
    const char& yyh::string::operator[](size_t pos) const
    {
    	return _str[pos];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.8 迭代器和const迭代器

    yyh::string::iterator yyh::string::begin()
    {
    	return _str;
    }
    
    yyh::string::iterator yyh::string::end()
    {
    	return _str + _size;
    }
    
    yyh::string::const_iterator yyh::string::begin() const
    {
    	return _str;
    }
    
    yyh::string::const_iterator yyh::string::end() const
    {
    	return _str + _size;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这里要注意iteratorconst_iterator都是typedef出来的。

    2.9 尾插字符

    void yyh::string::push_back(const char ch)
    {
    	if (_size == _capacity)
    	{
    		//扩容
    		if (_capacity == 0)
    		{
    			_capacity = 4;
    		}
    		_capacity *= 2;
    		char* tmp = new char[_capacity + 1];
    		strcpy(tmp, _str);
    		delete[]_str;
    		_str = tmp;
    	}
    	_str[_size] = ch;
    	_str[_size + 1] = '\0';
    	_size++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这里要注意的是:

    1️⃣ 扩容时_capacity是否为0?
    2️⃣ 结尾补充’\0’

    2.10 尾插字符串

    void yyh::string::append(const char* str)
    {
    	size_t sz = strlen(str);
    	if (_size + sz > _capacity)
    	{
    		//扩容
    		if (_capacity == 0)
    		{
    			_capacity = 4;
    		}
    		while (_capacity < _size + sz)
    		{
    			_capacity *= 2;
    		}
    		char* tmp = new char[_capacity + 1];
    		strcpy(tmp, _str);
    		delete[]_str;
    		_str = tmp;
    	}
    	strcpy(_str + _size, str);
    	_size += sz;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    尾插字符串首先要算出字符串的长度再看扩容多少。

    2.11 +=重载

    yyh::string& yyh::string::operator+=(const char ch)
    {
    	push_back(ch);// 复用
    	return *this;
    }
    
    yyh::string& yyh::string::operator+=(const char* str)
    {
    	append(str);// 复用
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.12 初始化扩容

    void yyh::string::resize(size_t n, char ch)
    {
    	if (n < _size)
    	{
    		_str[n] = '\0';
    		_size = n;
    	}
    	if (n > _size)
    	{
    		if (n > _capacity)
    		{
    			//扩容
    			if (_capacity == 0)
    			{
    				_capacity = 4;
    			}
    			while (_capacity <= n)
    			{
    				_capacity *= 2;
    			}
    			char* tmp = new char[_capacity + 1];
    			strcpy(tmp, _str);
    			delete[]_str;
    			_str = tmp;
    		}
    		for (int i = _size; i < n; i++)
    		{
    			_str[i] = ch;
    		}
    		_size = n;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    分情况讨论:

    n < _size 时直接在n的位置补'\0'
    n == _size 时不用处理
    n > _size 时要先扩容再补充字符

    2.13 查找字符 & 字符串

    size_t yyh::string::find(const char ch)
    {
    	size_t i = 0;
    	while (i < _size)
    	{
    		if (_str[i] == ch)
    		{
    			return i;
    		}
    	}
    	return npos;
    }
    
    size_t yyh::string::find(const char* str, size_t pos)
    {
    	const char* p = strstr(_str + pos, str);
    	if (p == nullptr)// 没找到
    		return npos;
    	return (p - _str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.14 插入字符

    yyh::string& yyh::string::insert(size_t pos, const char ch)
    {
    	assert(pos <= _size);
    	if (_size + 1 > _capacity)
    	{
    		//扩容
    		size_t tmp = _size + 1;
    		resize(tmp);
    	}
    	size_t end = _size + 1;
    	while (end > pos)
    	{
    		_str[end] = _str[end - 1];
    		end--;
    	}
    	_str[pos] = ch;
    	_size++;
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这里要注意while循环时,如果使用_str[end + 1] = _str[end];的形式就会出错,因为size_t是无符号数,恒大于0。

    2.15 插入字符串

    yyh::string& yyh::string::insert(size_t pos, const char* str)
    {
    	assert(pos <= _size);
    	size_t sz = strlen(str);
    	if (_size + sz > _capacity)
    	{
    		// 扩容
    		size_t tmp = _size + sz;
    		resize(tmp);
    		/*if (_capacity == 0)
    		{
    			_capacity = 4;
    		}
    		while (_capacity < _size + sz)
    		{
    			_capacity *= 2;
    		}
    		char* tmp = new char[_capacity + 1];
    		strcpy(tmp, _str);
    		delete[]_str;
    		_str = tmp;*/
    	}
    	size_t end = _size + sz;
    	while (end >= pos + sz)
    	{
    		_str[end] = _str[end - sz];
    		end--;
    	}
    	for (size_t i = pos; i < sz + pos; i++)
    	{
    		_str[i] = *str;
    		str++;
    	}
    	_size += sz;
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    这里要注意在while循环时结束条件是:end < pos + sz
    而且最后不能使用strcpy,会导致\0也被拷贝

    2.16 删除

    yyh::string& yyh::string::erase(size_t pos, size_t len)
    {
    	assert(pos < _size);
    	if (pos + len >= _size)
    	{
    		_size = pos;
    		_str[_size] = '\0';
    	}
    	else
    	{
    		strcpy(_str + pos, _str + pos + len);
    		_size -= len;
    	}
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    不能忘记补充'\0'

    2.17 输入<< 输出>> 重载

    ostream& operator<<(ostream& out, yyh::string& s)
    {
    	for (auto e : s)// 不能用c_str()
    	{
    		out << e;
    	}
    	return out;
    }
    
    istream& operator>>(istream& in, yyh::string& s)
    {
    	s._str[0] = '\0';//清空数据
    	s._size = 0;
    	char ch = in.get();
    	while (ch != '\n' && ch != ' ')
    	{
    		s += ch;
    		ch = in.get();
    	}
    	return in;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    输出的时候不能使用c_str(),因为有可能输出的字符串比_size大。



    纸上得来终觉浅,绝知此事要躬行。

  • 相关阅读:
    网络安全基础 之 防火墙 双机热备、防火墙类型、组网方式、逻辑区域划分、防火墙的各种NAT配置方法
    淘宝、京东、苏宁、拼多多、1688各大电商API接口详情( API 返回值说明,数据分析)
    [第二章—Spring MVC的高级技术] 2.1Spring MVC配置的替代方案
    证券期货业数据安全管理 与保护指引 附下载地址
    同行评审的度量与分析
    vscode的Emmet语法失效
    LeetCode【394】字符串解码
    Visual Studio 2022安装教程
    牛顿法与拟牛顿法摘记
    pysimpleGui 使用之sg.SaveAs使用
  • 原文地址:https://blog.csdn.net/qq_66314292/article/details/126864392