C++string类模拟实现

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

C++string类模拟实现

收录于专栏【C++语法基础
本专栏旨在分享学习C++的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

前置说明:

1. string类对象的常见构造

1.1 构造函数

1.2 拷贝构造

1.3 析构函数

1.4 赋值运算符

2. string类的迭代器

2.1 begin

2.2 end

3. string类对象容量操作

3.1 size

3.2 reserve

3.3 clear

4. string类对象的访问操作

4.1 operator[]

5. string类对象的增删查改

5.1 operator+=

5.2 append

5.3 push_back

5.4 insert

5.5 erase

5.6 swap

6. string类对字符串进行操作

6.1 c_str

6.2 find

6.3 substr

7. string类中的常量值

7.1 npos

8. string类中的运算符重载

9. string类中的输入输出流 


前置说明:

这里需要模拟实现string的操作有:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace my_string
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin();
		iterator end();

		const_iterator begin() const;
		const_iterator end() const;

		//string();
		string(const char* str = "");
		string(const string& s);
		string& operator=(const string& s);
		~string();

		const char* c_str() const;

		size_t size() const;
		char& operator[](size_t pos);
		const char& operator[](size_t pos) const;

		void reserve(size_t n);

		void push_back(char ch);
		void append(const char* str);

		string& operator+=(char ch);
		string& operator+=(const char* str);

		void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		void erase(size_t pos = 0, size_t len = npos);

		size_t find(char ch, size_t pos = 0);
		size_t find(const char* str, 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 _buff[16];
		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);
}

1. string类对象的常见构造

1.1 构造函数

	string::string(const char* str)
		:_size(strlen(str))
	{
		_str = new char[_size + 1];
		_capacity = _size;
		strcpy(_str, str);
	}

内存的大小是 _size + 1,多出的一字节用于存储字符串的终止空字符 \0。

1.2 拷贝构造

	// s2(s1)
	string::string(const string& s)
	{
		_str = new char[s._capacity + 1];
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}

注意:

这里的拷贝构造不能省略!!!

如果这里省略的话,编译器会调用默认的拷贝构造,最终导致的问题是:s1、s2共用同一块内 存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。


浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规.

1.3 析构函数

	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = _capacity = 0;
	}

1.4 赋值运算符

	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;
	}

2. string类的迭代器

2.1 begin

	string::iterator string::begin()
	{
		return _str;
	}

	string::const_iterator string::begin() const
	{
		return _str;
	}

注意:

这里实现了begin方法的两个版本: 一个是普通的成员函数,返回一个iterator,另一个是const成员函数,返回一个const_iterator.下面end同理. 


这样实现的原因:

    主要原因是为了支持不同的使用场景和对象状态。普通的 begin 方法返回一个可修改的迭代器,适用于非 const 对象,这样可以对字符串内容进行修改。另一方面,const 成员函数的 begin 方法返回一个只读的迭代器,适用于 const 对象,确保在遍历时不能修改字符串。这种设计保持了代码的一致性和灵活性。

2.2 end

	string::iterator string::end()
	{
		return _str + _size;
	}

	string::const_iterator string::end() const
	{
		return _str + _size;
	}

3. string类对象容量操作

3.1 size

	size_t string::size() const
	{
		return _size;
	}

3.2 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;
		}
	}

当n <= capacity时,reserve是不会缩小空间的 

3.3 clear

	void string::clear()
	{
		_str[0] = '\0';
		_size = 0;
	}

4. string类对象的访问操作

4.1 operator[]

	char& string::operator[](size_t pos)
	{
		assert(pos < _size);
		return _str[pos];
	}

5. string类对象的增删查改

5.1 operator+=

	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}

	string& string::operator+=(const char* str)
	{
		append(str);

		return *this;
	}

operator+=一个字符是通过push_back完成

operator+=一个字符串是通过append完成

5.2 append

	void string::append(const char* str)
	{
		insert(_size, str);
	}

5.3 push_back

	void string::push_back(char ch)
	{
		insert(_size, ch);
	}

5.4 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;
	}

 这段代码插入字符 ch 到字符串的指定位置 pos。
如果当前容量已满,则扩展容量以确保有足够的空间。
通过移动字符,为插入的字符腾出位置。
更新 _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;
}

检查位置有效性: 确保插入位置不超过当前字符串长度。
计算插入字符串长度: 获取要插入字符串的长度。
检查并扩展容量: 如果需要,扩展 string 对象的容量。
移动字符: 从字符串的末尾开始,将字符向后移动以腾出插入位置。
复制插入字符串: 将要插入的字符串复制到指定位置。
更新长度: 增加字符串的长度以反映插入的内容。

5.5 erase

	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;
		}
	}

如果 len 大于或等于从 pos 开始到字符串末尾的字符数(即 len >= _size - pos),删除 len 个字符会把从 pos 开始到字符串末尾的所有字符都删除。此时,将 \0(空字符)放在 pos 位置,以表示字符串的新结束位置,然后将 _size 更新为 pos,表示新的字符串长度。


如果 len 小于 pos 到字符串末尾的字符数,使用 strcpy 将从 pos + len 开始的字符复制到 pos 位置,从而覆盖删除的字符。然后,将 _size 减去 len,以反映删除后的字符串长度。 

5.6 swap

	void string::swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}

6. string类对字符串进行操作

6.1 c_str

	const char* string::c_str() const
	{
		return _str;
	}

6.2 find

	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);
		if (p == nullptr)
			return npos;
		return  p - _str;
	}

查找字符:

功能: 从字符串中查找字符 ch,从位置 pos 开始。
遍历 : 从 pos 开始遍历字符串,直到 _size(字符串末尾),检查每个字符是否等于 ch。
找到字符 : 如果找到了匹配的字符,返回其索引 i。
未找到 : 如果遍历结束都未找到字符,返回 npos,表示没有找到。


查找字符串:

功能: 从字符串中查找子字符串 sub,从位置 pos 开始。
使用 strstr : strstr 函数用于在 _str + pos 位置后的子字符串中查找 sub。strstr 返回指向子字符串首次出现的位置的指针。
计算位置 : 通过计算 p - _str,将找到的子字符串位置转换为相对于字符串开始的位置(即从 0 开始的索引)。
返回 : 返回子字符串的起始位置。如果 sub 未找到,则 strstr 返回 NULL

6.3 substr

string string::substr(size_t pos, size_t len)
{
	// len大于后面剩余字符,有多少取多少
	if (len > _size - pos)
	{
		string sub(_str + pos);
		return sub;
	}
	else
	{
		string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}

		return sub;
	}
}

功能: 从原字符串中提取一个子字符串。
处理超长情况 : 如果请求的长度 len 超过了从 pos 开始到字符串末尾的剩余字符数,则创建一个包含从 pos 到末尾的所有字符的子字符串。
处理正常情况 : 如果 len 不超过剩余字符数,则逐个字符地构造子字符串,并使用 reserve 提高效率。
效率 : 通过使用 reserve 和逐个字符追加,代码在正常情况下效率较高,但如果 len 超过剩余字符数,则会直接利用 string 的构造函数创建子字符串,这样可能在某些实现中效率更高。 

7. string类中的常量值

7.1 npos

	const size_t string::npos = -1;

8. string类中的运算符重载

	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);
	}

9. string类中的输入输出流 

	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;
	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值