【C++】初识STL库与模拟string类的底层实现

C++入门系列:
【C++】从C丝滑入门C++——初始C++
【C++】类和对象(一)
【C++】类和对象(二)
【C++】类和对象(三)
【C++】动态内存管理以及模板

加粗样式>—

感兴趣的同学点击支持一下!


初识STL

我们首先要知道什么是 STL :STL 是C++标准库的重要组成部分,里面包含了我们常用的数据结构和算法的框架。使用 STL 我们可以非常方便的进行数据结构方面的代码编写。

STL 由几大组件构成:
在这里插入图片描述
这里的组件笔者会通过学习为大家一一讲解,我们有一个概念即可。

sting类的模拟实现

我们首先学习的结构就是字符类—— string 。它内部包含了字符数组的增删查改、遍历、反转以及交换。这些功能我们都可以使用系列内置函数来进行实现。在本文笔者通过模拟实现一个 string 类的方法来带大家理解并使用 sting 类。

我们先看看其内部的成员函数以及构造函数:

namespace mystd
{
	class string 
	{
	public:
		string(const char* str = "");
		string(const string& s);
		string& operator=(const string& s);
		~string();
		
	private:
		char* _str;
		size_t _capacity;
		size_t _size;
	};
}

其实 string 类就是一个存储字符数组的顺序表,成员变量由指向数组空间的 _str 指针,空间容量 _capacity 以及字符个数 _size 构成。所以其构造函数与顺序表的初始化也类似,大家想了解顺序表的内容可以看笔者数据结构部分的文章——【数据结构】顺序表

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

		strcpy(_str, str);
	}

	string::string(const string& s)
	{
		_size = s._size;
		_capacity = s._capacity;
		_str = new char[_capacity + 1];

		strcpy(_str, s._str);
	}

	string& string::operator=(const string& s)
	{
		if (this != &s)
		{
			_size = s._size;
			_capacity = s._capacity;

			delete[] _str;
			_str = new char[_capacity + 1];
			
			strcpy(_str, s._str);
		}
		return *this;
	}

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

构造函数非常简单,即开辟动态内存,初始化 _capacity 与 _size 的大小。而这里需要注意的是拷贝构造函数与赋值重载函数的编写——我们需要对 string 类进行深拷贝,而不能简单的浅拷贝,浅拷贝在拷贝后容易造成内存的重复释放!!! 所以我们对深拷贝的内容单独开辟了一处内存空间,然后再进行值拷贝。

这样我们基础的框架以及写完,但是一个数据结构免不了增删查改,所以我们要对代码进行一一补充。首先要对这个对象进行增加字符串,我们免不了对字符数组的扩容问题,所以我们首先编写扩容函数 reserve。

在c语言中我们要对动态内存进行扩容,我们可以使用 realloc 函数。那么在 C++ 中有相应的函数或者关键字吗?遗憾的是没有。那我们用 realloc 进行扩容吗,我们之前提到在 C++ 编写代码时,我们最好使用配套的内存开辟与释放的方法。所以我们仍然使用 new 进行扩容。

我们首先 new 一整块我们需要的空间大小,然后我们可以对之前使用的空间进行释放,再让 _str 指针指向新开辟的内存空间区域。这样我们就完成了对内存的扩容。

namespace mystd
{
	if (n > _capacity)
	{
		_capacity = n;
		char* tmp = new char[_capacity + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
	}
}

可以对类对象进行扩容后,我们就可以轻松完成 string 类的插入工作:

namespace mystd
{
	void string::push_back(char c)
	{
		if (_size == _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newcapacity);
			_capacity = newcapacity;
		}
		_str[_size++] = c;
		_str[_size] = 0;
	}

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

	void string::append(const char* s)
	{
		size_t len = strlen(s);
		if ((_size + len) > _capacity)
		{
			_capacity = _size + len;
			reserve(_capacity);
		}
		strcpy(_str + _size, s);
		_size += len;
		_str[_size] = 0;
	}

	string& string::operator+=(const char* s)
	{
		append(s);
		return *this;
	}
}

插入无非就是首先检查内存空间大小是否够用,若不够用的话我们就进行扩容,再进行插入。本次实现了 += 的操作符重载的方式,也可进行对字符与字符串的扩容。

string str("123");
str += 4;			//扩容一个字符
str += "abcd";		//扩容一个字符串

删除也很简单,对一个顺序表我们需要删除只需改变 _size 的值即可。对一个字符类我们还需多实现一步对 _size 位置的数组置为0,这样的话我们打印字符串时到 \0 结尾也能适用此字符类。

namespace mystd
{
	string& string::erase(size_t pos, size_t len)
	{
		assert(pos <= _size);
		
		if (len == npos || pos + len >= _size)
		{
			_str[pos] = 0;
			_size = pos;
 		}
		else 
		{
			size_t end = pos + len;
			while (_str[end])
			{
				_str[end - len] = _str[end];
				end++;
			}
			_size = _size - len;
			_str[_size] = 0;
		}
		return *this;
	}
}

那么我们如何改变 string 类的字符内容呢,我们实现了一个[]的操作符重载函数,返回对应的引用内容,这样我们就可以对 string 类的内容查看与更改。

namespace mystd
{
	char& string::operator[](size_t index)
	{
		return *(_str + index);
	}
}

使用[]的操作符重载的好处是我们可以想操作数组一样操作这个对象,大大提高了代码的可读性:

string str("123");
str[1] = a;			//将2改为a字符

剩下的内容思路与其上类似,大家可以自己将下列代码实现一下:
笔者的实现内容大家可以翻阅笔者的gittee查看:https://gitee.com/zwz04/linux_learning

namespace mystd
{
	class string 
	{
	public:
		friend std::ostream& operator<<(std::ostream& out, const string& s);
		friend std::istream& operator>>(std::istream& in, const string& s);
		string(const char* str = "");
		string(const string& s);
		string& operator=(const string s);
		~string();

		void push_back(char c);
		string& operator+=(char c);
		void append(const char* s);
		string& operator+=(const char* s);
		void swap(string& s);
		const char* c_str() const;

		size_t size() const;
		size_t capacity() const;
		bool empty() const;
		void resize(size_t n, char c = '\0');
		void reserve(size_t n);

		char& operator[](size_t index);
		const char& operator[](size_t index) const;

		size_t find(char c, size_t pos = 0) const;
		size_t find(const char* s, size_t pos = 0) const; 
		string& insert(size_t pos, char c);
		string& insert(size_t pos, const char* s);
		string& erase(size_t pos, size_t len = npos);
		string substr(size_t pos, size_t len = npos) 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;
		bool operator<(const string& s) const;

		static size_t npos;
	private:

		char* _str;
		size_t _capacity;
		size_t _size;
	};

	void test1();
}
  • 38
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

楼鱼睡觉的猫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值