C++ Primer Plus 学习笔记(七)——STL

1 auto_ptr

auto_ptr模板定义了类似指针的对象,是一种智能指针,当auto_ptr对象过期时,其析构函数将使用delete来释放内存。

void test1()
{
    int* ip = new int(10); //动态内存未释放
    return;
}

void test2()
{
    auto_ptr ip(new int(10)); //auto_ptr对象过期后,会释放动态内存
    return;
}

注意,只能对new分配的内存使用auto_ptr对象,而不要对由new[]分配的或通过声明变量分配的内存使用它。

2 STL

STL提供了一组表示容器、迭代器、函数对象和算法的模板。

STL是一种通用编程技术,模板提供了存储在容器中的数据类型的通用表示,迭代器则是遍历容器中的值的通用表示。

2.1 容器

2.1.1 容器概念

容器概念描述了所有容器类都通用的元素,换句话说,容器概念指定了所有STL容器类都必须满足的一系列要求。

C++ Primer Plus 学习笔记(七)——STL_第1张图片

可以通过添加要求来改进基本的容器概念。

2.1.2 容器类型

各种容器模板都接受一个可选的模板参数,该参数指定使用哪个分配器对象来管理内存,例如vector模板:

template >

class vector : protected _Vector_base<_Tp, _Alloc> {…}

deque、list、queue、priority_queue、stack 和 vector 都是序列类型容器。

序列(sequence)概念增加了迭代器至少是正向迭代器的要求,以保证元素按特定的顺序排列,不会在两次迭代之间发生变化。

set、multiset、map 和 multimap 都是联合类型容器。

联合容器将值与关键字关联在一起,使用关键字来查找值。

2.2 迭代器

STL定义了5种迭代器——输入迭代器、输出迭代器、正向迭代器、双向迭代器、随机访问迭代器。

为何需要这么多迭代器呢?目的是为了在编写算法时尽可能使用要求最低的迭代器,并让它适用于容器的最大区间。

  • 输入迭代器:对于单通行、只读算法,可以使用输入迭代器。
  • 输出迭代器:对于单通行、只写算法,可以使用输出迭代器。
  • 正向迭代器:具有输入/输出迭代器的所有特性,同时还支持多次通行算法。
  • 双向迭代器:具有正向迭代器的所有特性,同时支持双向遍历容器。
  • 随机访问迭代器:具有双向迭代器的所有特性,同时添加了支持随机访问的操作和用于对元素进行排序的关系操作符。

C++ Primer Plus 学习笔记(七)——STL_第2张图片

STL根据所需的迭代器类型对算法进行了描述,例如 find() 的原型指出,这种算法需要一个输入迭代器。

template 
InputIterator find (InputIterator first, InputIterator last, const T& value);
2.2.1 指针作为迭代器

迭代器是广义指针,而指针满足所有的迭代器要求,因此STL算法可以使用指针来对基于指针的非STL容器进行操作。

例如,使用 sort() 对数组进行排序:

double nums[10];

sort(nums, nums + 10);

2.2.2 其他迭代器

头文件还提供了 ostream_iterator、istream_iterator、reverse_iterator、back_insert_iterator、front_insert_iterator 和 insert_iterator 这些专用的预定义迭代器类型。

2.3 函数对象

很多STL算法都使用函数对象——也叫函数符(functor)。函数符是可以以函数方式与()结合使用的任意对象,这包括函数名、指向函数的指针和重载了()操作符的类对象(即定义了函数operator()()的类)。

2.3.1 函数符概念

STL也定义了函数符概念:

  • 生成器:不用参数就可以调用的函数符。
  • 一元函数:用一个参数可以调用的函数符。
  • 二元函数:用两个参数可以调用的函数符。

对应的改进版概念:

  • 返回bool值的一元函数是断言。
  • 返回bool值的二元函数是二元断言。

例如, sort() 函数可以将二元断言作为其第3个参数:

bool Compare(const Book& book1, const Book& books);
...
sort(books.begin(), books.end(), Compare);
2.3.2 预定义函数符

为了支持将函数作为参数的STL函数,STL定义了多个基本函数符,它们执行诸如将两个值相加、比较两个值是否相等操作。

C++ Primer Plus 学习笔记(七)——STL_第3张图片

例如,假设需要对两个数组进行相加,普通实现和使用预定义函数符的实现为:

#include 
#include 
#include 
#include 
#include 

using namespace std;

double add(double d1, double d2) { return d1 + d2; }

int main()
{
    ostream_iterator out(cout, " ");
    vector ds1 = { 1.1, 2.2, 3.3, 4.4 };
    vector ds2 = { 1, 2, 3, 4 };

    // 1、普通实现必须为每种类型单独定义一个函数
    transform(ds1.begin(), ds1.end(), ds2.begin(), out, add);

    cout << endl;

    // 2、更好的办法是定义一个模板,而STL就已经预定义好了这么一个模板
    plus add2;
    transform(ds1.begin(), ds1.end(), ds2.begin(), out, add2);
}
2.3.3 函数适配器(已废弃)

如果 STL 函数需要一个一元函数,而现有一个能完成相应工作的自适应二元函数,则可以使用 bind1st 或 bind2nd 使该二元函数适应于一元接口。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

double sqrt(double d1) { return d1 * 2.5; }

int main()
{
    ostream_iterator out(cout, " ");
    vector ds1 = { 1.1, 2.2, 3.3, 4.4 };

    // 1、普通实现
    transform(ds1.begin(), ds1.end(), out, sqrt);

    cout << endl;

    // 2、另外一种实现是,将现有能实现该功能的二元函数适配为一元函数
    transform(ds1.begin(), ds1.end(), out, binder1st>(multiplies(), 2.5));
}

2.4 算法

对于算法函数设计,有两个主要的通用部分。首先,它们都使用模板来提供通用类型;其次,它们都使用迭代器来提供访问容器中数据的通用表示。

2.4.1 算法组

STL 将算法库分为4组:

  • 非修改式序列操作。
  • 修改式序列操作。
  • 排序和相关操作。
  • 通用数字计算。
2.4.2 函数和容器方法

相较于 STL 函数,STL 方法通常是更好的选择。首先,它更适合于特定的容器;其次,作为成员函数,它可以使用模板类的内存管理工具从而在需要时调整容器的长度。

2.5 其他库

Complex 头文件为复数提供了一个 complex 类模板,专用于 float、long 和 long double。

valarray 头文件提供了 valarray 模板类,被设计成用于表示数值数组,支持各种数值数组操作。

你可能感兴趣的:(c++,学习,算法)