探索C++标准模板库(STL):String接口的底层实现(下篇)

**前引:**在C++的面向对象编程中,对象模型是理解语言行为的核心。无论是类的成员函数如何访问数据,还是资源管理如何自动化,其底层机制均围绕两个关键概念展开:this指针与六大默认成员函数。它们如同对象的"隐形守护者",默默支撑着代码的健壮性与效率。本文将从技术底层出发,结合内存布局、编译器行为与实际案例,深入探讨!

本文目的:

在上篇我们学了各种string接口,此篇用来模拟实现各种接口功能,深入底层了解它的实现,来加深对它的理解,以及功能的熟悉

目录

String模拟实现

打印

访问字符

迭代器

插入字符/字符串

从pos位置插入字符

从pos位置插入字符串

从pos位置删除字符

从pos位置删除一定字符

寻找标记指定位置

截取指定区间

调整指定大小的空间

调整指定大小的空间+初始化


String接口模拟实现

打印

我们可以利用流提取来打印,返回字符串的指针就OK了,可以加上const更完美一点,如下:

cpp 复制代码
//打印
const char* Read()const
{
	return _allocator;
}

cout提取到char *会默认当做字符串处理

访问字符

在库里面这种[ ]访问是有两个版本的,一种是只读的,一种是可读可写的,如下:

所以我们要实现两个版本,编译器也会自己去匹配对应的函数:

左边的const 是防止返回的对象被修改;右边的const是防止this指针指向的对象被修改

cpp 复制代码
const char& operator[](int size)const
{
	//检查
	assert(size <= _size && size >= 0);
	return _allocator[size];
}
char& operator[](int size)
{
	//检查
	assert(size <= _size && size >= 0);
	return _allocator[size];
}
迭代器

迭代器有两种形式,一种是从前往后访问,一种是从后往前访问,我们这里来实现第一种

其实iterator是char *类型的,只是在库里面被重定义了,然后实现 begin、end即可

cpp 复制代码
iterator begin()
{
	return _allocator;
}
iterator end()
{
	return _allocator + _size;
}
const iterator begin()const
{
	return _allocator;
}
const iterator end()const
{
	return _allocator + _size;
}

效果展示:

auto 的底层其实就是迭代器,"换汤不换药",例如:

插入字符/字符串

开空间:

插入字符/字符串只是插入的个数不同,所以我们可以根据插入的个数来选择扩容+拷贝原来字符串

cpp 复制代码
//开空间
void reserve(size_t pos)
{
	char* tmp = nullptr;
	try
	{
		//开空间(传来的是最小空间,还要有\o)
		tmp = new char[pos + 1];
	}
	catch (const exception& _allocator)
	{
		cout << "空间增容失败" << endl;
	}
	//拷贝原字符
	strcpy(tmp, _allocator);
	//添加\0
	size_t size = strlen(tmp);
	tmp[size + 1] = '\0';
	//转移指向
	delete[]_allocator;
	_allocator = tmp;
}
void push_back(char c)
{
	size_t pos = strlen(_allocator) + 1;
	reserve(pos);
}
void append(const char* stl)
{
	size_t pos = strlen(_allocator) + strlen(stl);
	reserve(pos);
}

存储:

根据传过来的字符/字符串在空间末尾添加上就行

cpp 复制代码
//存储
void operator+=(char p1)
{
	_allocator[_size++] = p1;
}
void operator+=(const char* p2)
{
	strcpy(_allocator + _size, p2);
}

效果展示:

从pos位置插入字符

在某个位置插入字符很简单,我们来实现从pos位置插入 n 个字符

插入流程:

判断pos的合法性、开空间、移动原来字符、插入新字符

注意:

(1)在里面需要注意下标的使用,传的pos究竟是下标还是字符个数

(2)开空间需要注意给\0留位置,以及自动设置末尾的\0

(3)需要更新元素个数

cpp 复制代码
//指定位置插入字符
void insert_t(int pos, int n , char c)
{
	//pos是个数不是下标
	//检查位置的合法性
	assert(pos >= 1 && pos <= _size);
	//开空间
	char* tmp = new char[n + _size +1 ];
	strcpy(tmp, _allocator);
	tmp[n + _size] = '\0';
	delete[]_allocator;
	_allocator = tmp;
	//更新_size
	_size += n;
	//移动原来的字符
	for (int i = (_size + n); (i-n) >= pos ;i--)
	{
		_allocator[i - 1] = _allocator[i - n - 1];
	}
	//插入新字符
	for (int i = pos; n > 0; i++)
	{
		_allocator[i - 1] = c;
		n--;
	}
}

效果展示:

从pos位置插入字符串

这个较于插入字符比较简单一些,开空间、插入字符串都没有什么大的变化,如下:

这种需要调试的多,重要的是下标关系,新手可能需要在草稿纸上演示

cpp 复制代码
//指定位置插入字符串
void insert(int pos, const char* _stl)
{
	//pos是个数不是下标
	//检查位置的合法性
	assert(pos >= 1 && pos <= _size);
	//开空间
	int n = strlen(_stl);
	char* tmp = new char[n + _size + 1];
	strcpy(tmp, _allocator);
	tmp[n + _size] = '\0';
	delete[]_allocator;
	_allocator = tmp;
	//更新_size
	_size += n;
	//移动原来的字符
	for (int i = (_size ); (i - n) >= pos; i--)
	{
		_allocator[i - 1] = _allocator[i - n - 1];
	}
	//插入新字符
	int j = 0;
	for (int i = pos; n > 0; i++)
	{
		_allocator[i-1] = _stl[j++];
		n--;
	}
}
从pos位置删除字符

模拟成员函数:

删除字符不需要去开空间,可以在目标位置插入\0即可,记得更新字符个数

cpp 复制代码
//指定位置删除字符
void erase(size_t pos)
{
	//判断位置有效性
	assert(pos >= 1 && pos <= _size);
	//删除
	_allocator[pos - 1] = '\0';
    _size--;
}
从pos位置删除一定字符

这个接口的实现需要挪动后面的元素,整体来说还是很简单的

注意:更新字符个数,新字符串的末尾添加\0

cpp 复制代码
void erase_t(size_t pos, size_t n)
{
	//判断位置有效性
	assert(pos >= 1 && (pos+n-1)<=_size);
	_size -= n;
	//挪动元素
	int i = 1;
	for (i = pos ; _allocator[i + n -1] != '\0' ;  i++)
	{
		_allocator[i - 1] = _allocator[i + n - 1];
	}
	//插上\0
	_allocator[i-1] = '\0';
}
寻找标记指定位置

模拟库函数:

此函数有多种形式,我们来实现最简单的一种:

给一个位置,从此位置开始寻找目标,如果找到了就返回目标开始的下标位置,否则返回npos

实现思路:

假设给了一个字符串,和开始寻找的位置,从开始位置开始搜寻,遇到可能相同的目标开始确认,找到则返回开始的下标位置,否则返回npos(npos是无符号整型的最大值)

cpp 复制代码
//找指定字符串
size_t find(size_t pos, const char* _stl)
{
	//判断位置的合法性
	assert(pos >= 1 && pos <= _size);
	//从指定位置开始找目标
	const char* pc = _stl;
	while (1)
	{
		//找到可能目标
		if (_allocator[pos - 1] == *_stl)
		{
			//确认目标
			int tmp = pos;
			pc = _stl;
			while (pc)
			{
				//开始逐一比较字符
				if (*pc == _allocator[tmp - 1])
				{
					pc++;
					if (*pc == '\0')
					{
						return pos;
					}
					tmp++;
					if (pos >= _size)
					{
						return npos;
					}
				}
				else
				{
					//如果不相等
					pos++;
					tmp = pos;
					break;
				}
			}
		}
		else
		{
			//如果没有找到,就继续,直到
			pos++;
			if (pos >= _size)
			{
				return npos;
			}
		}
	}
	return npos;
}

效果展示:

截取指定区间

此函数通常是与上面标记的函数同时使用,由标记的函数先找指定位置,然后再截取,需要注意给的区间的有效性

原理:

截取一端区间打印即可

cpp 复制代码
//截取指定位置
void substr(size_t p1, size_t p2)
{
	//判断范围
	assert(p2 <= _size && p1 >= 1);
	size_t pos = 1;
	//找左区间
	while (_allocator[p1 - 1] != _allocator[pos-1])
	{
		pos++;
	}
	//开始打印
	while ((pos-1) != p2)
	{
		cout << _allocator[pos-1];
		pos++;
	}
	cout << endl;
}

效果展示:

调整指定大小的空间

我们模拟的resize,输入空间大小,将自动开辟指定大小的空间,没有初始化操作

cpp 复制代码
//开空间
void reserve(size_t pos)
{
	char* tmp = nullptr;
	try
	{
		//开空间(传来的是最小空间,还要有\o)
		tmp = new char[pos + 1];
	}
	catch (const exception& _allocator)
	{
		cout << "空间增容失败" << endl;
	}
	//拷贝原字符
	strcpy(tmp, _allocator);
	//添加\0
	size_t size = strlen(tmp);
	tmp[pos] = '\0';
	//转移指向
	delete[]_allocator;
	_allocator = tmp;
}
调整指定大小的空间+初始化

调整规则:

如果 size_t 大于当前字符串长度,则将剩余的字符初始化为指定的内容,否则转为空格

如果 size_t 小于当前字符串长度,则缩短为其前 n 个字符,并删除超出第 n 个字符的字符

如果小于_size直接缩短即可,如果大于我们又要麻烦的分情况,所以我们统一处理为开空间

cpp 复制代码
void resize(size_t n, char c = '\0')
{
	//对n判断
	assert(n >= 1);
	//判断n的大小
	if (n < _size)
	{
		//缩短字符串
		_allocator[n - 1] = '\0';
	}
	else
	{
		//开空间
		reserve(n);
		//初始化
		for (size_t i = _size; i < n; i++)
		{
			_allocator[i] = c;
		}
	}
}

效果展示:

【雾非雾】期待与你的下次相遇!

相关推荐
jie18894575866几秒前
C++ 中的 const 知识点详解,c++和c语言区别
java·c语言·c++
网安INF5 分钟前
RSA加密算法:非对称密码学的基石
java·开发语言·密码学
明月*清风6 分钟前
c++ —— 内存管理
开发语言·c++
蔡蓝10 分钟前
设计模式-观察着模式
java·开发语言·设计模式
西北大程序猿1 小时前
单例模式与锁(死锁)
linux·开发语言·c++·单例模式
你不是我我1 小时前
【Java开发日记】说一说 SpringBoot 中 CommandLineRunner
java·开发语言·spring boot
心扬1 小时前
python网络编程
开发语言·网络·python·tcp/ip
qq_454175791 小时前
c++学习-this指针
开发语言·c++·学习
尘浮7282 小时前
60天python训练计划----day45
开发语言·python
sss191s2 小时前
校招 java 面试基础题目及解析
java·开发语言·面试