STL初探

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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【UE4 RTS】07-Camera Boundaries

前言 本篇实现的效果是当CameraPawn移动到地图边缘时会被阻挡。 效果 步骤 1. 打开项目设置&#xff0c;在“引擎-碰撞”中&#xff0c;点击“新建Object通道” 新建通道命名为“MapBoundaries”&#xff0c;然后点击接受 2. 向视口中添加 阻挡体积 调整阻挡体积的缩放 向四…

孤立随机森林(Isolation Forest)(Python实现)

目录 1 简介 2 孤立随机森林算法 2.1 算法概述 2.2 原理介绍 2.3 算法步骤 3 参数讲解 4 Python代码实现 5 结果 1 简介 孤立森林&#xff08;isolation Forest&#xff09;是一种高效的异常检测算法&#xff0c;它和随机森林类似&#xff0c;但每次选择划分属性和划…

Zookeeper 面试题

一、ZooKeeper 基础题 1.1、Zookeeper 的典型应用场景 Zookeeper 是一个典型的发布/订阅模式的分布式数据管理与协调框架&#xff0c;开发人员可以使用它来进行分布式数据的发布和订阅。 通过对 Zookeeper 中丰富的数据节点进行交叉使用&#xff0c;配合 Watcher 事件通知机…

EFLFK——ELK日志分析系统+kafka+filebeat架构

环境准备 node1节点192.168.40.16elasticsearch2c/4Gnode2节点192.168.40.17elasticsearch2c/4GApache节点192.168.40.170logstash/Apache/kibana2c/4Gfilebeat节点192.168.40.20filebeat2c/4G https://blog.csdn.net/m0_57554344/article/details/132059066?spm1001.2014.30…

设计模式(2)工厂方法模式

一、 1、介绍&#xff1a;定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断&#xff0c;根据客户端的选择条件动态实例化相关的类&#xff0c;对于客户端来说…

UML-状态图

目录 状态图 状态图的图符 状态机 状态 ​转换 电话机状态图 活动图和状态图区别&#xff1a; 状态图 状态图(Statechart Diagram)是描述一个实体基于事件反应的动态行为&#xff0c;显示了该实体如何根据当前所处的状态对不同的事件做出反应。通常我们创建一个UML状态…

中级课程——CSRF

文章目录 案例原理挖掘 案例 原理 挖掘 挖掘详情 首先就是对目标敏感部位进行抓包分析&#xff0c;比如修改信息、转账、添加信息等等。通常一个数据包HTTP请求头里边都会有一个Referer&#xff0c;这个需要特别去验证。比如放到Burpsuit Repeater里边去测试&#xff1a;去掉…

语音同声翻译软件助你跨越语言障碍

嘿&#xff0c;你在日常工作中是否曾经参加过跨国会议&#xff0c;是否也曾由于语言不通而感到尴尬&#xff1f;别担心&#xff0c;因为现在有了会议实时翻译软件&#xff0c;这些问题都将成为过去式&#xff01;那么你知道会议实时翻译的软件有哪些吗&#xff1f;接下来就让我…

无涯教程-Perl - mkdir函数

描述 此功能使用MODE指定的模式创建一个名称和路径EXPR的目录,为清楚起见,应将其作为八进制值提供。 语法 以下是此函数的简单语法- mkdir EXPR,MODE返回值 如果失败,此函数返回0,如果成功,则返回1。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl -w$dirname &…

【计算机视觉|生成对抗】条件生成对抗网络(CGAN)

本系列博文为深度学习/计算机视觉论文笔记&#xff0c;转载请注明出处 标题&#xff1a;Conditional Generative Adversarial Nets 链接&#xff1a;[1411.1784] Conditional Generative Adversarial Nets (arxiv.org) 摘要 生成对抗网络&#xff08;Generative Adversarial…

最大子数组和——力扣53

文章目录 题目描述解法一 动态规划题目描述 解法一 动态规划 int maxSubArray(vector<int>& nums){int pre=0, res=nums

自动测试框架airtest应用一:将XX读书书籍保存为PDF

一、Airtest的简介 Airtest是网易出品的一款基于图像识别和poco控件识别的一款UI自动化测试工具。Airtest的框架是网易团队自己开发的一个图像识别框架&#xff0c;这个框架的祖宗就是一种新颖的图形脚本语言Sikuli。Sikuli这个框架的原理是这样的&#xff0c;计算机用户不需要…

微软杀入Web3:打造基于区块链的AI产品

作者&#xff1a;秦晋 2023年1月&#xff0c;微软向 ChatGPT 创建者 OpenAI 投资 100 亿美元&#xff0c;在AI业界引发格外关注。此举也让微软在AI的战略探索上提前取得有利位置。 2023年3月&#xff0c;微软软件工程师 Albacore 披露微软正在为Edge 浏览器测试内置的非托管加密…

Linux 共享内存mmap,进程通信

文章目录 前言一、存储映射 I/O二、mmap&#xff0c; munmap三、父子进程间 mmap 通信四、非血缘关系进程间 mmap 提通信五、mmap 匿名映射区总结 前言 进程间通信是操作系统中重要的概念之一&#xff0c;使得不同的进程可以相互交换数据和进行协作。其中&#xff0c;共享内存…

安达发|企业如何提高生产实现精细化管理

随着市场竞争的加剧&#xff0c;企业如何提高生产效率和降低成本成为了关键。本文将探讨生产计划排程表的制定方法&#xff0c;帮助企业实现精细化管理&#xff0c;提升竞争力。 在传统的生产管理中&#xff0c;企业往往依赖于人工经验和直觉来制定生产计划&#xff0c;导致生产…

刷题笔记 day9

1658 将 x 减到 0 的最小操作数 解析&#xff1a;1. 当数组的两端的数都大于x时&#xff0c;直接返回 -1。 2. 当数组所有数之和小于 x 时 &#xff0c;直接返回 -1。 3. 数组中可以将 x 消除为0&#xff0c;那么可以从左边减小为 0 &#xff1b;可以从右边减小为 0 &#xff1…

深眸科技|发现AI+3D视觉的价值,技术升级加速视觉应用产品国产替代

随着中国工业化进程的不断深入和智能制造浪潮的影响&#xff0c;工业生产对于机器视觉技术的需求不断攀升&#xff0c;其应用范围覆盖了工业领域的众多行业&#xff0c;包括3C电子、汽车、半导体、新能源、物流等。 据GGII发布的最新数据显示&#xff0c;近年来我国机器视觉市…

数据结构篇七:排序

文章目录 前言1.插入排序1.1 基本思想1.2 代码实现1.3 特性总结 2.希尔排序2.1 基本思想2.2 代码实现2.3 特性总结 3. 选择排序3.1 基本思想3.2 代码实现3.3 特性总结 4. 堆排序4.1 基本思想4.2 代码实现4.3 特性总结 5. 冒泡排序5.1 基本思想5.2 代码实现5.3 特性总结 6. 快速…

机器学习笔记之优化算法(十三)关于二次上界引理

机器学习笔记之优化算法——关于二次上界引理 引言回顾&#xff1a;利普希兹连续梯度下降法介绍 二次上界引理&#xff1a;介绍与作用二次上界与最优步长之间的关系二次上界引理证明过程 引言 本节将介绍二次上界的具体作用以及它的证明过程。 回顾&#xff1a; 利普希兹连续…

【Java】智慧工地云平台源码-支持私有化部署+硬件设备

智慧工地硬件设备包括&#xff1a;AI识别一体机、智能广播音响、标养箱、塔机黑匣子、升降机黑匣子、吊钩追踪控制设备、扬尘监测设备、喷淋设备。 1.什么是AI危险源识别 AI危险源识别是指基于智能视频分析技术&#xff0c;对视频图像信息进行自动分析识别&#xff0c;以实时监…