《C++ Primer Plus》学习笔记 — 标准模板库

本文详细介绍了C++ Primer Plus中的STL部分,包括迭代器的使用,如前缀和后缀版本的++,不同类型的迭代器及其层次结构;容器的概念和类型,如序列容器和关联容器;函数对象,包括预定义的函数符和自适应函数符;以及常见的STL算法。此外,还提及了valarray和C++11的模板initializer_list。
摘要由CSDN通过智能技术生成


STL是一种泛型编程。面向对象关注的是编程的数据方面,而泛型编程关注的是算法。它们之间的共同点是抽象和创建可重用代码,但它们的理念绝然不同。—— 《C++ Primer Plus》

一、迭代器

1、为何使用迭代器

模板使得容器能够独立于存储的数据类型,而迭代器使算法独立于使用的容器类型。模板使得类在编译具体化时才关注具体的模板参数,而迭代器则将数据的存储方式和访问处理分离。
如果我们想在数组和链表中查找某个数值,其实现可能如下:

#include <iostream>
using namespace std;

struct Node
{
   
	Node* next;
	int item;
};

int main()
{
   
	int arr[]{
    1,2,3 };
	Node* nodeStart;
	Node* nodeEnd;
	...
	
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
   
		if (arr[i] == 3)
		{
   
			cout << "find 3 at " << i << endl;
		}
	}

	for (Node* node = nodeStart; node != nullptr; node = node->next)
	{
   
		if (node->item == 3)
		{
   
			cout << "find 3 at " << node << endl;
		}
	}
}

使用迭代器后,代码可以被优化为:

int main()
{
   
	vector<int> vec{
    1,2,3 };
	list<int> li{
    1,2,3 };
	
	auto iterVec = find(vec.begin(), vec.end(), 3);
	auto iterList = find(li.begin(), li.end(), 3);
}

find正是根据STL根据iterator所提供的搜索函数:

template <class _InIt, class _Ty>
_NODISCARD _CONSTEXPR20 _InIt find(_InIt _First, const _InIt _Last, const _Ty& _Val) {
    // find first matching _Val
    _Adl_verify_range(_First, _Last);
    _Seek_wrapped(_First, _Find_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Val));
    return _First;
}

这里vec.end() 一般指向尾指针,一个不存在的元素,也就是我们通常所说的哨兵节点。这里尾指针的存在并不是对迭代器的要求,而是对容器类的要求。

2、++的前缀和后缀版本

_Vector_iterator& operator++() noexcept {
   
    _Mybase::operator++();
    return *this;
}

_Vector_iterator operator++(int) noexcept {
   
    _Vector_iterator _Tmp = *this;
    _Mybase::operator++();
    return _Tmp;
}

通过是否带一个参数int区分。不带int为前缀,带int为后缀。

3、迭代器类型

不同的算法对迭代器的要求不同。查找算法需要遍历迭代器,因此需要迭代器实现++运算符;它只需要读取数据而不需要修改数据。排序算法要求能够随机访问,以交换不同位置的元素,因此需要实现+运算符;它需要能够写数据,因此要重写=运算符。STL定义了五种迭代器:输入迭代器、输出迭代器、正向迭代器、双向迭代器和随机访问迭代器。下面我们学习下几种迭代器的特征:
输入迭代器 — 支持读取数据;通过支持++运算符实现访问其中所有元素;并不保证每次遍历容器所得到的元素顺序相同;迭代器递增之后不能保证之前的值仍可用;不能逆向访问。
输出迭代器 — 支持写入数据;其余同输入迭代器
正向迭代器 — 支持读取和写入数据;通过支持++运算符实现访问其中所有元素;保证每次遍历容器所得到的元素顺序相同;迭代器递增之后之前的值仍可用。
双向迭代器 — 同时支持++和–操作符;其余同正向迭代器
随机访问迭代器 — 支持随机访问(如下图),其余同双向迭代器
在这里插入图片描述
这里不同的迭代器只是一种概念性描述。为了提高效率,我们应该在编程中使用要求最低的迭代器。我们看看一些不同层次的迭代器的使用:

#include <iostream>
#include <vector>
#include <list>
#include <forward_list>
using namespace std;

int main()
{
   
	// LegacyRandomAccessIterator
	vector<int> vec{
    1,2,3 };
	auto iterRandom = vec.begin();
	iterRandom += 10;

	// LegacyBidirectionalIterator
	list<int> li{
    1, 2, 3 };
	auto iterBi = li.begin();
	iterBi--;
	//li += 10; invalid

	// LegacyForwardIterator
	forward_list <int> liFw{
    1, 2, 3 };
	auto iterForward = liFw.begin();
	iterForward++;
	//iterForward--; invalid
}
}

4、迭代器层次结构

在这里插入图片描述

5、concept refinement model

STL使用术语concept来描述一系列的要求。概念可以具有类似继承的关系。这就是说我们可以认为双向迭代器继承了正向迭代器的功能。但是我们不能将继承机制用于迭代器。因为迭代器可以用内置类型实现。例如,我们可以使用类实现正向迭代器,而使用指针实现双向迭代器。因此,我们使用refinement描述这种概念上的继承。概念的具体实现被称为model。因此指向某种类型的指针是一个随机访问迭代器的模型。
C++20中引入了关键字concept,其作用正对应于我们上面所提到的术语concept

#include <iostream>
#include <concepts>
using namespace std;

class CLS_DisableCopy
{
   
public:
	int a;
	CLS_DisableCopy() {
   }
	CLS_DisableCopy(const CLS_DisableCopy&) = delete;
};

template<copy_constructible T>
T f(T a)
{
   
	T b(a);
	return b;
}

int main()
{
   
	f<string>("abc");

	CLS_DisableCopy obj;
	obj.a = 1;
	auto copy = f<CLS_DisableCopy>(obj); // invalid
}

这里的copy_constructible就是使用关键字concept定义的一种规范。不满足此规范的类不能被应用与函数模板上。现在,我们使用迭代器相关的concept重新定义我们的findsort函数。

#include <iterator>
#include <concepts>
#include <list>
using namespace std;

template <input_iterator _TIterator, class _TValue>
_TIterator myFind(const _TIterator &start, const _TIterator &end, const _TValue &value)
{
   
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值