STL初探

news2024/10/5 17:19:56

STL简介

STL(standard template libaray - 标准模板库)是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

STL的一些版本

  • 原始版本

Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本即所有STL实现版本的始祖。

  • P. J. 版本

由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。

  • RW版本

由Rouge Wage公司开发,继承自HP版本,不能公开或修改,可读性一般。

  • SGI版本

由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版本。被GCC采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。

STL的6大组件

STL的一些缺陷

  1. STL库的更新太慢了。上一版靠谱是C++98,中间的C++03基本一些修订。C++11出来已经相隔了13年,STL才进一步更新。
  2.  STL不支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。
  3.  STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。
  4.  STL的使用会有代码膨胀的问题,比如使用vector<int> / vector<double> / vector<char> 这样会生成多份代码,当然这是由于模板语法本身导致的。

                                                                                                        ——截至2023-8-9

string

string的使用

用法参考网址:cplusplus.com/reference/string/string/

string常用API - 用法示例

  • part1:构造与赋值
string s1;			// 无参构造
string s2(10, 65);	// 有参构造,开局10个'A'
string s3("hello world");	// 拷贝构造
s1 = s3;	// operator =
string s4(s3, 2, 5);
const char* str = "abcdefg";
string s5(str);
string s6(str, 5);
  • part2:迭代器

begin():指向容器第一个元素的位置(可读可写)
rbegin():指向容器最后一个元素的位置(可读可写)
cbegin():指向容器第一个元素的位置(只读)
crbegin():指向容器最后一个元素的位置(只读)


end():指向容器最后一个元素的下一个位置(可读可写)
rend():指向容器第一个元素的前一个位置(可读可写)
cend():指向容器最后一个元素的下一个位置(只读)
crend():指向容器第一个元素的前一个位置(只读)

// 正向非常量迭代器,可以正常读写
string str1("hello world");
string::iterator beg = str1.begin();
while (beg != str1.end())
{
	cout << ++ * beg << " ";
	beg++;
}
cout << endl;

// 反向非常量迭代器,反方向正常读写
string str2("hello world");
string::reverse_iterator rbeg = str2.rbegin();
while (rbeg != str2.rend())
{
	cout << ++ * rbeg << " ";
	rbeg++;
}
cout << endl;

// 正向常量迭代器,只能读不能写
string str3("hello world");
string::const_iterator cbeg = str3.cbegin();
while (cbeg != str3.end())
{
	//cout << ++*cbeg << " ";	// error
	cout << *cbeg << " ";
	cbeg++;
}
cout << endl;

// 反向常量迭代器,反方向只读不写
string str4("hello world");
string::const_reverse_iterator crbeg = str4.crbegin();
while (crbeg != str4.crend())
{
	//cout << ++*crbeg << " ";	// error
	cout << *crbeg << " ";
	crbeg++;
}
cout << endl;
  • part3:容量相关
string str = "hello world";
// size方法:Return length of string(不包含最后的'\0')
cout << "str_size: " << str.size() << endl;
// length方法与size方法效果相同
cout << "str_length: " << str.length() << endl;
// max_size方法:Return maximum size of string(没有实际的参考性)
cout << "str_max_size: " << str.max_size() << endl;
// resize方法:改变size,但不改变容量大小
// capacity方法:获取string的容器大小
str.resize(5);
cout << str << endl;
cout << "str_size: " << str.size() << endl;
cout << "str_capacity: " << str.capacity() << endl;
// reverse方法:尝试更改容量大小(只有当变大的时候生效)
str.reserve(5);
cout << "change_5_capacity_result: " << str.capacity() << endl;
str.reserve(20);
cout << "change_20_capacity_result: " << str.capacity() << endl;
// clear方法:清空string
str.clear();
// empty方法:判空
cout << str.empty() << endl;
// shrink_to_fit方法:尝试缩容 
// 函数原型:void shrink_to_fit();
// This function has no effect on the string length and cannot alter its content.
str = "123456789";
cout << "size: " << str.size() << "capacity: " << str.capacity() << endl;
str.shrink_to_fit();
cout << "size: " << str.size() << "capacity: " << str.capacity() << endl;

part4:数据访问

string str = "hello world";
cout << str[3] << endl;  //operator[] - 越界触发断言
cout << str.at(7) << endl;   // at方法 - 越界抛异常
cout << str.back() << endl;   // back方法:访问最后一个字符
cout << str.front() << endl;  // front方法:访问第一个字符

part5:数据操作

string s1 = "hello ";
string s2 = "world";
// operator+=
s1 += s2;
cout << s1 << endl;
// append:在字符串尾添加字符串或单个字符
s2.append(" good");
cout << s2 << endl;
// push_back:尾插单个字符
s1.push_back('!');
cout << s1 << endl;
// assign:赋值,效果与operator=一样
string s3;
s3.assign(s2);
cout << s3 << endl;
// insert:在任意位置插入(下标从0开始)
s3.insert(5, "*******");
cout << s3 << endl;
// erase:按下标/迭代器的位置删除n个
s3.erase(5, 3); // 从下标5开始,删除3个
cout << s3 << endl;
// replace:将指定位置的n个字串替换为另一段字符串
s3.replace(5, 3, " *-* ");
cout << s3 << endl;
// swap:交换两个string对象。对应的size和capacity也原封不动的交换
s3.swap(s2);
cout << s2 << endl;
// pop_back:尾删单个字符
s3.pop_back();
cout << s3 << endl;

string注意事项

  1. 注意resize方法和reverse方法,区分size和capacity:size返回的是字符串长度,而capacity返回的是容器的容量大小。同理。resize调整的是字符串长度,而reverse是尝试调整容量的大小。(reverse并不会改变字符串的内容)
  2. string和其它容器不同。string实际上是basic_string的char型的具体模板类:

    所以这就是为什么string每次使用时不需要显示的指定类型,而其它容器(vector、list等)都需要显示的指定数据类型的原因了。
  3. 其实不光有string一种字符串容器,根据需要我们还可以选择u16string、u32string、wstring等。

模拟实现string

模拟实现string有助于我们更好的理解string的相关功能,更好的体会STL容器的设计。如下是我自己写的部分string功能:

// 输出输出流的重载
ostream& ytc::operator<<(ostream& _cout, const ytc::string& s)
{
	if (s.empty()) //避免cout一个已释放的string
		return _cout << "";
	return _cout << s._str;
}
istream& ytc::operator>>(istream& _cin, ytc::string& s)
{
	s.clear();
	char get = _cin.get();
	while (get != '\n'/* || get != ' '*/) //可以选择读不读空格
	{
		if (s._size == s._capacity)
			s.reserve((s._capacity + 1) * 2);
		s._str[s._size++] = get;
		get = _cin.get();
	}
	return _cin;
}

// 构造和析构
ytc::string::string(const char* str) : _size(strlen(str)), _capacity(strlen(str))
{
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}
ytc::string::string(const ytc::string& s)
{
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}
ytc::string& ytc::string::operator=(const string& s)
{
	// 容量检查
	size_t len = strlen(s._str);
	reserve(_size + len);
	// 拷贝内容
	strcpy(_str, s._str);
	_size = s._size;
	return *this;
}
ytc::string::~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

// iterator
ytc::string::iterator ytc::string::begin()
{
	return _str;
}
ytc::string::iterator ytc::string::end()
{
	return _str + _size;
}

// modify
void ytc::string::push_back(char c)
{
	// 容量检查
	if (_size == _capacity)
		reserve((_capacity + 1) * 2);
	_str[_size++] = c;
	_str[_size] = '\0';
}
ytc::string& ytc::string::operator+=(char c)
{
	push_back(c);
	return *this;
}
void ytc::string::append(const char* str)
{
	// 容量检查
	size_t len = strlen(str);
	reserve(_size + len);
	// 追加内容
	strcat(_str, str);
	_size += len;
}
ytc::string& ytc::string::operator+=(const char* str)
{
	append(str);
	return *this;
}
void ytc::string::clear()
{
	memset(_str, 0, sizeof(char) * _size);
	_size = 0;
}
void ytc::string::swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_capacity, s._capacity);
	std::swap(_size, s._size);
}
const char* ytc::string::c_str()const
{
	return _str;
}

// capacity
size_t ytc::string::size()const
{
	return _size;
}
size_t ytc::string::capacity()const
{
	return _capacity;
}
bool ytc::string::empty()const
{
	return _size == 0;
}
void ytc::string::resize(size_t n, char c)
{
	// n > _size,尾插字符
	if (n > _size)
	{
		// 一步到位的扩容
		reserve(n + 1);
		// 尾插字符
		int times = n - _size;
		while (times--)
		{
			// 每次添加字符检查一下的扩容
			/*if (_size == _capacity)
			{
				_str[_size] = '\0'; //最后加个'\0',防止拷贝的数组比实际的空间大的问题
				reserve(2 * (_capacity + 1));
			}*/
			_str[_size++] = c;
		}
		_str[_size] = '\0';
	}
	// n < _size,直接截断
	else if (n < _size)
	{
		_str[n] = '\0';
		_size = n;
	}
}
void ytc::string::reserve(size_t n)
{
	if (n > _capacity)
	{
		char* temp = new char[n + 1];
		strcpy(temp, _str);
		delete[] _str;
		_str = temp;
		_capacity = n;
	}
}

// access
char& ytc::string::operator[](size_t index)
{
	// 先将*this转换为const对象,然后调用const对象的operator[]
	// 然后再将其返回的const char转换为char
	return const_cast<char&>(
		static_cast<const ytc::string&>(*this)[index]);
}
const char& ytc::string::operator[](size_t index)const
{
	assert(index < _size);
	return _str[index];
}

//relational operators
bool ytc::string::operator<(const string& s)
{
	return strcmp(_str, s._str) < 0;
}
bool ytc::string::operator<=(const string& s)
{
	return _str < s._str || _str == s._str;

}
bool ytc::string::operator>(const string& s)
{
	return strcmp(_str, s._str) > 0;
}
bool ytc::string::operator>=(const string& s)
{
	return _str > s._str || _str == s._str;
}
bool ytc::string::operator==(const string& s)
{
	return strcmp(_str, s._str) == 0;
}
bool ytc::string::operator!=(const string& s)
{
	return !(_str == s._str);
}

// 返回c在string中第一次出现的位置(下标)
size_t ytc::string::find(char c, size_t pos) const
{
	if (pos < _size)
	{
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == c)
				return i;
		}
	}
	return npos;
}
// 返回子串s在string中第一次出现的位置
size_t ytc::string::find(const char* s, size_t pos) const
{
	const char* res = strstr(_str + pos, s);
	if (res != nullptr)
	{
		return (res - _str) / sizeof(_str[0]);
	}
	return npos;
}
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
ytc::string& ytc::string::insert(size_t pos, char c)
{
	// 如果pos传的过大了,那么就直接在最后插入
	if (pos > _size)
		pos = _size;
	// 容量检查
	if (_size == _capacity)
		reserve((_capacity + 1) * 2);
	// 字符后移
	size_t index = _size + 1;
	while (index >= pos && index + 1 != 0) //后面的条件是控制0位置的特殊情况
	{
		_str[index] = _str[index - 1];
		index--;
	}
	_str[pos] = c;
	_size++;
	// 最后返回
	return *this;
}
ytc::string& ytc::string::insert(size_t pos, const char* str)
{
	// 如果pos传的过大了,那么就直接在最后插入
	if (pos > _size)
		pos = _size;
	// 容量检查
	size_t len = strlen(str);
	reserve(_size + len);
	size_t index = _size;
	// 字符后移
	while (index >= pos && index + 1 != 0) //后面的条件是控制0位置的特殊情况
	{
		_str[index + len] = _str[index];
		index--;
	}
	strncpy(_str + pos, str, len);
	_size += len;
	// 最后返回
	return *this;
}
// 删除pos位置上的元素,并返回该元素的下一个位置
ytc::string& ytc::string::erase(size_t pos, size_t len)
{
	if (pos < _size)
	{
		if (len > _size || pos + len > _size)
		{
			len = _size - pos + 1;
		}
		size_t index = pos;
		while (index < _size)
		{
			_str[index] = _str[index + len];
			index++;
		}
		_str[index] = '\0';
		_size -= len;
	}
	else
	{
		cout << "删除位置错误!" << endl;
	}
	return *this;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/866381.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go语言从0基础到安全项目开发实战

一.环境搭建并helloworld 搭建环境比较简单 1.1安装SDK 到以下链接下 Go下载 - Go语言中文网 - Golang中文社区 下载windows版本64位zip包 https://studygolang.com/dl/golang/go1.20.7.windows-amd64.zip 1.2配置环境变量 不配置的话就只能在bin目录下才能运行go命令 …

使用埋点方式对应用监控

在指标监控的世界里&#xff0c;应用和业务层面的监控有两种典型手段&#xff0c;一种是在应用程序里埋点&#xff0c;另一种是分析日志&#xff0c;从日志中提取指标。埋点的方式性能更好&#xff0c;也更灵活&#xff0c;只是对应用程序有一定侵入性&#xff0c;而分析日志的…

Monge矩阵

Monge矩阵 对一个m*n的实数矩阵A&#xff0c;如果对所有i&#xff0c;j&#xff0c;k和l&#xff0c;1≤ i<k ≤ m和1≤ j<l ≤ n&#xff0c;有 A[i,j]A[k,l] ≤ A[i,l]A[k,j] 那么&#xff0c;此矩阵A为Monge矩阵。 换句话说&#xff0c;每当我们从矩阵中挑…

如何查看防火墙3306端口是否开放,如何开放

1、查看端口是否开放&#xff1a; 可以通过命令行或者telnet方式进行查看。 命令行&#xff1a; 在Windows系统的命令提示符中输入“netstat -an”(不带引号)即可查看到所有打开的端口号&#xff0c;其中3306是MySQL数据库服务的默认端口号&#xff0c;如果显示“LISTENING”则…

Vue生命周期函数(详解)

目录 生命周期图 生命周期函数 beforeCreate和created的区别 beforeCreate创建前应用场景 created创建后应用场景 beforeMount和mounted的区别 beforeMount挂载前应用场景 mounted挂载后应用场景 beforeUpdate和updated的区别 beforeUpdate更新前应用场景 updated更新后应用…

Linux —— 基础I/O(二)

目录 一&#xff0c;FILE 二&#xff0c;缓冲区 三&#xff0c;重定向 系统调用dup2 一&#xff0c;FILE FILE结构体内部包括 变量_fileno&#xff0c;即对应的文件描述符下标fd&#xff1b;应用层C语言提供的缓冲区数据&#xff1b;其IO相关函数与系统调用接口对应&#…

《面试1v1》ElasticSearch倒排索引

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…

2021年06月 C/C++(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题:数的输入和输出 输入一个整数和双精度浮点数,先将浮点数保留2位小数输出,然后输出整数。 时间限制:1000 内存限制:65536 输入 一行两个数,分别为整数N(不超过整型范围),双精度浮点数F,以一个空格分开。 输出 一行两个数,分别为保留2位小数输出的F,以及整数N,以…

机器学习实战3-随机森林算法

文章目录 集成算法概述sklearn中的集成算法模块 RandomForestClassifier重要参数&&随机森林的分类器控制基评估器的参数n_estimatorssklearn建模流程复习交叉验证我们进行10次交叉验证&#xff0c;观察随机森林和决策树的效果n_estimators学习曲线 bootstrap & oob…

Spring Boot | 使用mkcert本地生成SSL证书配置后端接口为HTTPS协议

Tips&#xff1a;本篇博客是 Windows 版本的使用教程&#xff0c;cmd 中执行的命令前缀是下载的软件名称&#xff0c;需要改成自己下载软件的名称&#xff01; 下载软件 首先去 GitHub 仓库中下载软件&#xff0c;下载完成后将文件保存在英文路径下的文件夹&#xff0c;之后以…

Pytorch源码搜索与分析

PyTorch的的代码主要由C10、ATen、torch三大部分组成的。其中&#xff1a; C10 C10&#xff0c;来自于Caffe Tensor Library的缩写。这里存放的都是最基础的Tensor库的代码&#xff0c;可以运行在服务端和移动端。PyTorch目前正在将代码从ATen/core目录下迁移到C10中。C10的代…

unity新输入系统的简单使用(New InputSystem)

1、在包管理器 unity注册表中下载安装InputSystem 2、给玩家添加组件PlayerInput&#xff0c;点击CreatAction,创建一个InputAct InputAct,这是玩家的输入文件&#xff0c;在里面可以设置玩家输入 3、使用 例如玩家控制角色移动 在InputAct中&#xff0c;默认已经设置好了移…

5.1 web浏览安全

数据参考&#xff1a;CISP官方 目录 Web应用基础浏览器所面临的安全威胁养成良好的Web浏览安全意识如何安全使用浏览器 一、Web应用基础 1、Web应用的基本概念 Web ( World wide Web) 也称为万维网 脱离单机Web应用在互联网上占据了及其重要的地位Web应用的发展&#xf…

使用next.js编写TodoList(连接了数据库)(提供源码)

准备 安装next可以查看nextjs入门使用_姚*鸿的博客的博客-CSDN博客 安装Prisma可以查看 使用Prisma访问数据库_姚*鸿的博客的博客-CSDN博客 确保你前面两个步骤做完。 再提醒以下记得修改数据库的信息&#xff1a; 源码地址 next-todolist: nextjs的todolist例子 效果演示 开始…

生信豆芽菜——箱线图+小提琴图使用说明

网站&#xff1a;http://www.sxdyc.com/diffBoxViolin 三、使用方法 1.打开网址&#xff08;http://www.sxdyc.com/singleCollectionTool?href-diff&#xff09;&#xff0c;选择“箱线图小提琴图” 准备数据,数据格式用为txt文本&#xff0c;以制表符分割 第一个文件为特征…

VS Code 使用cnpm下载包失败

一、 问题如下&#xff1a; 网上找到的解决方法是要在powershell中执行&#xff1a; Set-ExecutionPolicy RemoteSigned进行更改策略。 首先我们解释下这个Set-ExecutionPolicy RemoteSigned&#xff0c;Set-ExecutionPolicy 是一个 PowerShell 命令&#xff0c;用于控制脚本…

基于ipad协议的gewe框架进行微信群组管理(二)

友情链接 geweapi.com 点击访问即可。 获取群组详情 小提示&#xff1a; 该接口可以一次查询20个群组查询出来的信息是不带公告的 请求URL&#xff1a; http://域名地址/api/group/detail 请求方式&#xff1a; POST 请求头&#xff1a; Content-Type&#xff1a;applica…

AI模型公司如何定位 ?

AI模型公司如何定位 ? 企业与消费者&#xff1f; 和 多用途与利基市场&#xff1f; 文本将分解每个象限。 消费类和多用途 最有价值的象限并引发了人工智能热潮。 顶级公司&#xff1a; Open AI - 通过 ChatGPT 为消费者构建&#xff0c;并通过其旗舰 GPT 模型为企…

【数据结构】反转链表、链表的中间节点、链表的回文结构(单链表OJ题)

正如标题所说&#xff0c;本文会图文详细解析三道单链表OJ题&#xff0c;分别为&#xff1a; 反转链表 &#xff08;简单&#xff09; 链表的中间节点 &#xff08;简单&#xff09; 链表的回文结构 &#xff08;较难&#xff09; 把他们放在一起讲的原因是&#xff1a; 反转链…

vray渲染如何设置?最佳 VRay 渲染设置

什么是 VRay 渲染设置&#xff1f; 让我们从基础开始吧。V-Ray 设置使您可以完全控制用于更改和调整渲染过程的参数。您可以通过“设置”选项卡中的“资源编辑器”找到它们&#xff0c;并且如您所见&#xff0c;它们组织在两个面板中。主面板显示场景设置的默认选项。 如果默认…