C++——模拟实现string

本文详细介绍了自定义my_string类的过程,包括构造函数、析构函数、拷贝构造、赋值运算符重载等基本成员函数,以及迭代器、容量管理、元素访问、字符串修改、关系运算符重载等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、定义my_string类

  此处标明:以下成员函数都是写在my_list类内部的

#include<iostream>
#include<assert.h>
#include<string>

using namespace std;

class my_string{
private:
	char* _ptr;    //字符串
	size_t _size;       //有效字符个数
	size_t _capacity;      //容量
	static size_t npos;     //size_t类型中最大数(表示-1)
public:
	//交换
	void Swap(my_string& str)
	{
		swap(_ptr, str._ptr);
		swap(_size, str._size);
		swap(_capacity, str._capacity);
	}
	//成员函数及其他功能在下面实现
};
size_t my_string::npos = -1;

二、成员函数

(一)构造函数

  对象中需要额外的资源,则显示写出构造函数;
  构造函数参数缺省一个空字符串,写成一个默认构造;

	my_string(char* str = "")
	{
		_size = strlen(str);
		_capacity = _size;
		
		_ptr = new char[_size + 1];    //开一个_size+1的空间,给'\0'留一个位置
		strcpy(_ptr, str);
	}

  构造函数中申请一个_size+1的空间,用来将字符串拷贝到对象中,留出一个 \0 的位置;

(二)析构函数

  对象中有额外的资源开销,需要显示写出析构函数;

	~my_string()
	{
		//对象资源不为nullptr,则进行销毁
		if (_ptr)
		{
			delete[] _ptr;
			_ptr = nullptr;

			_size = 0;
			_capacity = 0;
		}
	}

(三)拷贝构造

  1、传统写法

	my_string(const my_string& str)
		:_ptr(new char[str._capacity + 1])   //此处开了str._capacity + 1的空间,是因为_capacity是最大有效容量,不包括'\0'
		, _size(str._size)
		, _capacity(str._capacity)
	{
		strcpy(_ptr, str._ptr);
	}

  2、简便写法
  简便写法就是先将对象本身初始化为空,再通过构造函数创建一个临时的对象,与对象本身交换以达到拷贝的效果;

	my_string(const my_string& str)
		:_ptr(nullptr)     //此处让_ptr = nullptr是为了销毁临时对象时不会造成销毁错误
		, _size(0)
		, _capacity(0)
	{
		//创建一个临时对象
		my_string tmp(str._ptr);

		//与临时对象进行交换
		Swap(tmp);
	}

  _ptr(nullptr) 这个得目的是临时对象在调用析构的时候也可以正常运行,如果不初始化为 nullptr 的话,则_ptr会指向内存中的地址,那么再析构销毁时就会出现问题;

(四)赋值运算符重载

  对象有资源开销,则显示写出赋值运算符重载函数,完成资源的深拷贝;
  1、传统写法

	my_string& operator=(const my_string& str)
	{
		//不是自己给自己赋值,则进入判断
		if (this != &str)
		{
			//开空间
			char* tmp = new char[str._capacity + 1];
			//释放原有空间
			delete[] _ptr;
			//拷贝
			strcpy(tmp, str._ptr);
			//改变指针指向,完成深拷贝
			_ptr = tmp;

			_size = str._size;
			_capacity = str._capacity;
		}

		return *this;
	}

  2、简便写法

	my_string& operator=(my_string str)
	{
		Swap(str);

		return *this;
	}

  这种写法的深拷贝在传参的时候就已经进行了,参数接收时用了一个拷贝构造拷贝了要赋值的对象,再将其与被赋值对象进行资源交换,则完成了深拷贝赋值;

三、迭代器

  迭代器底层上其实就是指针,不过是将指针起了一个别名叫iterator,所以实现时也就是用指针实现的;
  begin其实就是获取第一个元素的地址;end是获取最后一个元素的下一个位置的地址;
  需要注意的是const迭代器必须用const来修饰函数;

	//迭代器iterator
	typedef char* iterator;
	typedef const char* const_iterator;

	iterator begin()
	{
		return _ptr;
	}
	iterator end()
	{
		return _ptr + _size;        //指向最后一个索引的下一个位置
	}

	const_iterator begin() const
	{
		return _ptr;
	}
	const_iterator end() const
	{
		return _ptr + _size;
	}

	const_iterator cbegin() const
	{
		return _ptr;
	}
	const_iterator cend() const
	{
		return _ptr + _size;
	}

四、Capacity

(一)获取string容量

  1、获取有效元素个数:size()

size_t size()
{
	return _size;
}

  2、获取最大有效元素容量:capacity()

size_t capacity()
{
	return _capacity;
}

  3、判空

	bool empty() const
	{
		if (_size == 0)
			return true;
		return false;
	}

(二)改变容量

  1、扩容:reserve()
    当容量不够时需要扩容,需要重新开辟一块更大的空间;

	void reserve(size_t n)
	{
		if (n >= _capacity)
		{
			//开空间
			char* tmp = new char[_capacity + 1];
			//拷贝
			strcpy(tmp, _ptr);
			//删除原有空间
			delete[] _ptr;
			//改变指向
			_ptr = tmp;

			_capacity = n;
		}
	}

  2、改变有效元素的个数:resize()
   改变容量resize通常有以下几方面的情况:

	(1)n < _size:缩容
	(2)_size < n < _capacity:增容 + 赋值
	(3)n > _capacity:扩容 + 增容 + 赋值
	void resize(size_t n, char ch = '\0')
	{
		if (n > _size)
		{
			if (n > _capacity)
			{
				reserve(n);
			}

			memset(_ptr + _size, ch, (n - _size) * sizeof(char));
		}

		_size = n;
		_ptr[_size] = '\0';
	}

五、元素访问

(一)[]运算符重载

	char& operator[](int i)
	{
		//判断位置是否越界
		assert(i < _size);

		return _ptr[i];
	}

	const char& operator[](int i) const
	{
		assert(i < _size);

		return _ptr[i];
	}

六、string修改

(一)插入insert

  1、插入单个字符
    在pos位置的前面插入一个字符;
    插入时,将待插入位置后面的字符从后向前依次向后移动;

	my_string& insert(size_t pos, char ch)
	{
		//判断访问是否越界
		assert(pos <= _size);

		//判断容器是否满了
		if (_size == _capacity)
		{
			size_t new_capacity = _capacity == 0 ? 15 : 2 * _capacity;
			reserve(new_capacity);
		}

		size_t end = _size;
		//从后向前移动元素
		while (end > pos)
		{
			_ptr[end] = _ptr[end - 1];
			
			end--;
		}

		_ptr[pos] = ch;
		_ptr[++_size] = '\0';       //最后需要在_size位置补上‘\0’

		return *this;
	}

  2、插入字符串
    在pos位置前面插入一个字符串;
    插入时,将待插入位置后面的字符从后向前依次向后移动;

	my_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 - 1;

		while (end > pos + len - 1)
		{
			_ptr[end] = _ptr[end - len];

			end--;
		}

		memcpy(_ptr + pos, str, len);
		
		_size += len;
		_ptr[_size] = '\0';

		return *this;
	}

(二)删除erase

  将pos位置向后n个字符删除,pos缺省为0,len缺省为最大值npos(也就是将从pos位置到字符串结尾全部删除);
  删除时,将删除的元素后面的元素从前向后向前移动;

	my_string& erase(size_t pos = 0, size_t len = npos)
	{
		//判断边界
		assert(pos < _size);
		//判断是否是无效删除位置
		if ((pos + len >= _size) || (len == npos))
		{
			_size = pos;
			_ptr[_size] = '\0';

			return *this;
		}

		size_t start = pos + len;
		//从前向后移动位置
		while (start < _size)
		{
			_ptr[start - len] = _ptr[start];
			start++;
		}

		_size -= len;
		_ptr[_size] = '\0';

		return *this;
	}

(三)尾插push_back

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

(四)尾删pop_back

	void pop_back()
	{
		erase(_size - 1);
	}

(五)追加插入append

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

(六)+=运算符重载

  +=运算会改变原对象的内容,所以返回引用

	my_string& operator+=(const my_string& str)
	{
		append(str._ptr);

		return *this;
	}

七、关系运算符重载

	//<运算符重载
	bool operator<(const my_string& str)
	{
		if (strcmp(_ptr, str._ptr) < 0)
			return true;
		return false;
	}
	
	//<=运算符重载
	bool operator<=(const my_string& str)
	{
		if (*this > str)
			return false;
		return true;
	}

	//>运算符重载
	bool operator>(const my_string& str)
	{
		if (strcmp(_ptr, str._ptr) > 0)
			return true;
		return false;
	}

	//>=运算符重载
	bool operator>=(const my_string& str)
	{
		if (*this < str)
			return false;
		return true;
	}

	//==运算符重载
	bool operator==(const my_string& str)
	{
		if (strcmp(_ptr, str._ptr) == 0)
			return true;
		return false;
	}

	//!=运算符重载
	bool operator!=(const my_string& str)
	{
		if (*this == str)
			return false;
		return true;
	}

八、查找

  1、查找一个字符
    从pos位置开始查找一个字符,pos缺省值为0;
    因为查找时不能修改字符串,所以需要用const来修饰该函数;
    返回c在string中第一次出现的位置

	// 返回c在string中第一次出现的位置
	size_t find(char ch, size_t pos = 0) const
	{
		size_t count = 0;

		while (count < _size)
		{
			if (*(_ptr + pos + count) == ch)
				return count;

			count++;
		}

		return count;
	}

  2、查找一个字符串
    从pos位置开始查找一个字符串,pos缺省值为0;
    因为查找时不能修改字符串,所以需要用const来修饰该函数;
    返回子串s在string中第一次出现的位置


	// 返回子串s在string中第一次出现的位置
	size_t find(const char* str, size_t pos = 0) const
	{
		size_t len = strlen(str);
		size_t count = 0;
		
		while (count < _size - len + 1)
		{
			size_t i = pos + count;

			while (i < count + len)
			{
				size_t j = 0;

				if (_ptr[i] != str[j])
					break;

				i++;
				j++;
			}

			if (i == count + len)
				return count;

			count++;
		}

		return count;
	}

九、输入输出运算符重载(不是成员函数)

  输入输出运算符重载函数不是成员函数,不要写到my_string类中;

(一)输入运算符重载

istream& operator>>(istream& _cin, my_string& str)
{
	char ch;

	while ((ch = getchar()) != EOF)
	{
		if (ch == '\n')
			return _cin;

		str += ch;
	}

	return _cin;
}

(二)输出运算符重载

ostream& operator<<(ostream& _cout, const my_string& str)
{
	for (const auto& ch : str)
	{
		_cout << ch;
	}

	return _cout;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值