STL之my_string容器(超详细版)

前言:上篇文章和大家分享了string容器的使用,但是如果我们只会使用,还不能足以支持我们在以后的工作和学习。所以我们必须要深挖string容器背后的底层原理,通过自己模拟实现一个简单的my_string从而加深对string的底层原理和string的使用的理解。

1.my_string实现的前提条件:

我们知道STL中已经帮我们实现好了string容器的各个接口,如果我们直接实现my_string就会和STL里面的string冲突,所以我们需要一个命名空间把my_string包围起来,防止和库里面的string冲突。

我们实现一个简单的stirng容器(包含构造函数,拷贝构造函数,析构函数,运算符重载,输出函数,求字符串的长度函数)

首先我们需要清楚string的成员变量里面需要什么,string是一个存放字符串的字符数组,所以我们可以在my_string的成员变量中定义一个指针,可以让这个指针进行访问字符串。

namespace ljy
{
	class string
	{
	private:
		char* _str;
	};
}

然后我们就可以实现string的构造函数了,string创建对象时可能是带参也可能是不带参,所以我们需要实现两个构造函数,但是在前面我们学过一个缺省参数,所以我们可以把这两个构造函数结合起来实现。

string构造对象时如果传的无参,我们就需要一个\0才能判断传参结束,所以这个缺省参数我们可以什么都不传,然后在开辟一个字节的空间存放\0就可以了

string构造对象时如果传的有参,我们知道带参的对象是放在常量区的,而常量区对象是不能被修改的,所以我们不能把对象放在常量区,以前学c语言时我们动态开辟空间都是在堆上,所以现在我们也可以在堆上开辟空间来存放字符串。
在这里插入图片描述

string(const char* str = "")//这里的缺省参数不需要任何东西
	:_str(new char[strlen(str) + 1])//开辟+1的空间存放\0
{
	//把数据拷贝到新的空间(strcpy会自动把\0拷贝过去)
	strcpy(_str, str);
}

接下来我们就实现求字符串长度的函数,这个函数非常的简单,我们可以直接使用strlen帮我们求字符串的长度就可以了。

	size_t size() const//求字符串的长度不需要修改字符串可以加const
	{
		return strlen(_str);
	}

再接着我们实现operator[ ]任意访问字符,这个函数也非常简单,我们只需返回_str[i]的结果就行了,我们返回的时this指针指向的字符串,所以出了该函数的作用域它还会存在,所以我们可以用引用返回,减少拷贝,提高代码效率。

char& operator[](size_t i)
{
	return _str[i];
}

实现一个析构函数,析构函数需要释放我们开辟的空间,并且让这个指针指向空

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

实现输出函数(实现类似于c_str接口的输出函数)

实现c_str的输出函数,我们只需要返回字符串的首元素地址就行了。

const char* c_str()
{
	return _str;
}

我们来测试一下

void test_string1()
{
	string s("hello");
	cout << s.c_str() << " ";
}

在这里插入图片描述

实现拷贝构造函数(s2(s1))

我们先不自己实现拷贝构造函数,看使用编译器默认的拷贝构造函数,看看会不会出现什么问题,如果不出现什么问题,那么我们不是可以直接用编译器默认生成的拷贝构造函数吗?
在这里插入图片描述
代码直接就崩掉了,为什么呢?这里是一个深浅拷贝的问题,编译器默认生成的拷贝构造函数是值拷贝,它会按字节把s2数据一个一个拷贝到s1中。
我给大家画个图大家就能理解了。
在这里插入图片描述
那么我们该如何解决这个问题呢?我们需要自己实现一个深拷贝构造函数,我们可以开辟一个和s1一样大的空间,然后把s2里面的字符串拷贝到新的空间。``

//s2(s1)
string(const string& s)
	//开辟一个和s1一样大的空间
	:_str(new char[strlen(s._str) + 1])
{
	//把s1的内容拷贝到s2中
	strcpy(_str, s._str);
}
	void test_string2()
	{
		string s1("hello");
		string s2(s1);
		cout << s2.c_str() << " ";
	}

在这里插入图片描述

实现operator= (s2=s1)

如果不实现operator也会出现浅拷贝问题,现在我们需要把s1赋值给s2,所以需要开辟一个和s1大小一样的空间,然后把s1里面的值拷贝到新的空间,然后把s2的旧空间释放掉,再把新空间给s2,最后再返回s2对象

//s2=s1(防止自己给自己赋值)
string& operator=(const string& s)
{

	//开辟一个和s1大小一样的新的空间,并把s1的内容拷贝到新的空间
	char* tmp = new char[strlen(s._str) + 1];
	strcpy(tmp, s._str);
	//释放s2指向的旧空间
	delete[] _str;
	//再把新的空间给s2
	_str = tmp;
	return *this;
}

测试一下

void test_string3()
{
	string s1("hello world");
	string s2;
	s2 = s1;
	cout << s2.c_str() << " ";
}

在这里插入图片描述

实现简单的string容器的全部代码

namespace ljy
{
	class string
	{
	public:
		string(const char* str = "")//这里的缺省参数不需要任何东西
			:_str(new char[strlen(str) + 1])//开辟+1的空间存放\0
		{
			//把数据拷贝到新的空间(strcpy会自动把\0拷贝过去)
			strcpy(_str, str);
		}

		size_t size() const//求字符串的长度不需要修改字符串可以加const
		{
			return strlen(_str);
		}
		char& operator[](size_t i)
		{
			return _str[i];
		}

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

		const char* c_str()
		{
			return _str;
		}

		//s2(s1)
		string(const string& s)
			//开辟一个和s1一样大的空间
			:_str(new char[strlen(s._str) + 1])
		{
			//把s1的内容拷贝到s2中
			strcpy(_str, s._str);
		}

		//s2=s1
		string& operator=(const string& s)
		{
			if (this != &s)
			{
			//开辟一个和s1大小一样的新的空间,并把s1的内容拷贝到新的空间
			char* tmp = new char[strlen(s._str) + 1];
			strcpy(tmp, s._str);
			//释放s2指向的旧空间
			delete[] _str;
			//再把新的空间给s2
			_str = tmp;
			return *this;
			}
		}
	private:
		char* _str;
	};

	void test_string1()
	{
		string s("hello");
		cout << s.c_str() << " ";
	}

	void test_string2()
	{
		string s1("hello");
		string s2(s1);
		cout << s2.c_str() << " ";
	}

	void test_string3()
	{
		string s1("hello world");
		string s2;
		s2 = s1;
		cout << s2.c_str() << " ";
	}
}

实现简单的string容器(现代写法)

现代写法:利用第三者为我去做我想做的事,然后等他做完我再把它的成果拿给我自己使用。

namespace ljy_copy
{
	class string
	{
	public:
		//构造函数
		string(const char* str)
			:_str(new char[strlen(str) + 1])
		{
			strcpy(_str, str);
		}

		//利用构造函数实现拷贝构造函数
		//s2(s)
		string(const string& s)
			:_str(nullptr)//先让_str指向空,防止由于时随机值,导致析构函数释放空间时出现问题
		{
			//利用tmp去构造一个和s1一样的对象
			string tmp(s._str);
			//再把tmp和s2交换
			swap(_str, tmp._str);
		}

		//利用拷贝构造函数实现赋值
		//s2=s
		string& operator[](string s)//string s会自动调用拷贝构造函数
		{
			swap(_str, s._str);
			return *this;
		}
	private:
		char* _str;
	};
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平平无奇。。。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值