详解C++模拟实现string类

目录

准备

构造函数

c_str

size

capacity

析构函数

拷贝构造

swap

operator[ ]

迭代器iterator的实现

begin

end

reserve

insert在任意位置插入字符/字符串

push_back

append

operator+

operator+=

erase

clear

pop_back

比较函数

substr

find(利用kmp算法实现)

kmp详解可移步至:KMP算法详解(一眼看穿next[ ]数组)

>>

<<


准备

为了自己创建的string类不和库当中的发生冲突,可以自己创建一个命名空间

namespace test
{

}

string需要三个参数,一个存放字符串(char* _str),一个存放大小(size_t _size),一个存放容量(size_t _capacity)

//自定义一个命名空间,防止与stl中的string同
namespace test
{
	class string
	{
	public:
		//构造函数
		string()
		{
			
		}

		//析构函数
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
	
}

构造函数

构造函数需要做的就是对成员进行初始化,由于字符串是常量,类型为const char*,所以接收的时候也需要用const char*来接收,但这里不能简单的直接将str给_str,因为str是存放在常量区的,如果直接将_str=str,会将str在常量区的地址也拷贝过去,会导致之后无法修改string的内容,所以需要给_str分配一个空间,然后再将str的值全部拷贝过去,在分配空间的时候要多分配一个空间给'\0'

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

为了方便测试函数是否正确,提供一个c_str函数来获取对象中的字符串

c_str

官方解释:

 函数类型是const char*因为这只是一个提取对象内容的函数,为了防止权限放大,因为某些原因导致指针被修改就麻烦大了

返回一个指针包括'\0'的字符串

		const char* c_str() const
		{
			//添加const防止修改指针的内容
			return _str;
		}

测试一下构造函数,传一个hello world

 

通过监视窗口可以看到是拷贝成功了的

运行结果:

 

 再提供两个接口来获取size和capacity

size

官方解释:

首先返回类型为size_t,大小肯定不会为负数,用const修饰
返回的是字符串的实际长度,不包括'\0',也不是容量的大小

		size_t size() const
		{
			return _size;
		}

capacity

官方解释:

 和size差不多,返回的容量不包括'\0'

        size_t capacity() const
		{
			return _capacity;
		}

运行结果:

 运行结果是正确的,但这里存在两个构造函数,第一种是默认的

string()
{

}

第二种就是我们自主实现的,其实这里可以简化一下,将两种合并为一种,在传的字符串为空的时候就会调用第一种构造函数,空的话字符串内容就是'\0',_capacity=0,_size=0,可以直接将第二种构造函数的参数给个缺省值,如果不填就默认是""或者"\0",这两种是等效的,第一种也是直接填一个'\0'上去,所以构造函数也可以写成:

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

或者:

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

析构函数

成员变量中_str是动态开辟出来的,所以需要对内存进行delete释放,释放后再将其指向nullptr空

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

拷贝构造

思路1:

对_str重新分配空间,然后利用strcpy函数来讲str中的字符串全部拷贝到_str中

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

测试:

运行结果:

思路2:

创建一个临时变量tmp来拷贝str,再通过swap函数来交换,因为tmp是临时变量,出了函数作用域就会调用析构函数来销毁tmp,所以在tmp和str交换之后,因为str的原地址需要被销毁,这里就不需要手动销毁了,我们将str的地址给到了tmp,出了函数作用域之后调用析构函数就将其销毁了。

		string(const string& str)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(str._str);
			swap(tmp);
		}

swap

全局的那个swap对于内置类型交换很快,但对于自定义类型会比较慢了,它会掉用三次构造函数,所以可以自己实现一个swap函数

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

 operator[ ]

对操作符[ ]进行重载,可以通过[ ]来获取字符

官方解释:

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

迭代器iterator的实现

iterator实质上就是一个类似指针的东西但又不是指针,但是在string类中可以将其当作指针,所以对iterator进行重命名即可


                
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值