《C++新经典》第19章 STL标准模板库大局观

《C++新经典》第19章 STL标准模板库大局观

  • 19.1 STL总述、发展史、组成与数据结构谈
    • 19.1.1 几个概念与推荐书籍
    • 19.1.2 算法和数据结构谈
    • 19.1.4 标准库使用
    • 19.1.5 STL的组成部分
  • 19.2 容器分类与array、vector容器精解
    • 19.2.1 容器的分类
    • 19.2.2 容器的说明和简单应用
  • 19.3 容器的说明和简单应用续
    • 19.3.1 deque和stack
    • 19.3.2 queue
    • 19.3.3 list
    • 19.3.4 其它
  • 19.4 分配器简介、使用与工作原理说
    • 19.4.1 分配器简介
    • 19.4.2 分配器的使用
    • 19.4.3 其它的分配器与原理说
  • 19.5 迭代器的概念和分类
    • 19.5.1 迭代器基本概念
    • 19.5.2 迭代器的分类
  • 19.6 算法简介、内部处理与使用范例
    • 19.6.1 算法简介
    • 19.6.2 算法内部一些处理
    • 19.6.3 一些典型算法使用范例
  • 19.7 函数对象回顾、系统函数对象与范例
    • 19.7.1 函数对象/仿函数回顾
    • 19.7.2 标准库中定义的函数对象
    • 19.7.3 标准库中定义的函数对象范例
  • 19.8 适配器概念、分类、范例与总结
    • 19.8.1 适配器基本概念
    • 19.8.2 容器适配器
    • 19.8.3 算法适配器
    • 19.8.4 迭代器适配器
    • 19.8.5 总结

19.1 STL总述、发展史、组成与数据结构谈

19.1.1 几个概念与推荐书籍

  1. C++标准库
    C++ Standard Library,一般安装C++编译器时都会安装。

  2. 标准模板库
    Standard Template Library(STL),包含在C++标准库中(核心)。

  3. 泛型编程
    Generic Programming,使用模板(Template)为主要编程手段来编写代码。

标准模板库就是用泛型编程的编码方式写的一套供程序员非常方便使用的库。

  1. 推荐书籍
    《C++标准库》、《STL源码剖析》。

19.1.2 算法和数据结构谈

  1. 数据结构浅谈
    数据结构,研究数据存取的学问。栈、队列、链表、散列表(哈希表)、树、图等,存取速度快慢,优劣势,具体使用场景等。

  2. 数据结构学习方法
    (1)栈:后进先出。
    (2)队列:先进先出。
    (3)链表:多个节点链(串)在一起,每个节点都有数据部分和next指针部分。
    (4)其它树、图、散列表等。

  3. 推荐书籍
    《算法导论》

19.1.4 标准库使用

#include 
#include 
#include 
#include 
using namespace std;

19.1.5 STL的组成部分

  1. 容器
    vetcor、map、list等。

  2. 迭代器
    用于遍历或者访问容器中的元素,类似指针。

  3. 算法(函数)
    STL提供的一些函数,用来实现一些功能,例如search(查找)、sort(排序)、copy(复制)等。

  4. 分配器
    不太常用,针对频繁分配小块内存时,减少内存空间的浪费,具有一定的提升分配内存效率的作用(内存分配器)。一般使用默认分配器(allocator)。

  5. 其它
    适配器、仿函数(函数对象)等。

19.2 容器分类与array、vector容器精解

19.2.1 容器的分类

容器,保存数据,用于管理一大群元素。

  1. 顺序容器
    Sequence Containers,array、vector、deque、queue、stack、list、forward_list。

  2. 关联容器
    Associative Containers,每一个元素都是一个键值(key/value)对。通过键来找元素,类似小数据库或小字典,查找快速。内部使用树的数据结构来存储数据,按照一定规则(比如根据key)自动存放元素位置,无法控制元素插入位置,只能控制插入内容。set、multiset、map、multimap。
    hash_set、hash_multiset、hash_map、hash_multimap等(已过时,用unordered_set、unordered_multiset、unordered_map、unordered_multimap替代)。

  3. 无序容器
    Unordered Contianers,属于关联容器的一种,只在意元素是否在容器内,内部用哈希表实现。
    unordered_set、unordered_multiset、unordered_map、unordered_multimap等。

19.2.2 容器的说明和简单应用

  1. array
    顺序容器,数组,空间连续,大小固定。

与内置数组比较:
(1)比内置数组更安全,内置数组不能直接使用拷贝和赋值,array数组可以。
(2)array下标必须是无符号类型,而数组则没有这个限制(可以负数)。

array<int, 10> ial1={0,1,2,3};
array<int, 10> copy=ial1;//只要保证两者的类型一致就可以(包括元素类型和大小)
#include 

array<string, 5> mystring = {"I", "love", "china"};
cout <<mystring.size()<<endl;//5
cout <<sizeof(mystring)<<endl;//不等于5,array类的大小
mystring[0] = "long long long";
  1. vector
    空间连续,动态大小,超过容量时,重新分配空间。
    建议vector内使用指针,或者提前分配空间。
#include 
#include 
using namespace std;

class A
{
public:
    int m_i;
    A(int tmp = 0) : m_i(tmp)
    {
        cout << "A(int)\n";
    }
    A(const A &a)
    {
        m_i = a.m_i;
        cout << "A(const A&) \n";
    }
    ~A()
    {
        cout << "~A()\n";
    }
};

#define N 3
int main()
{
    {
        //vector veca(N);
        vector<A> veca;
        veca.reverse(N);
        for (int i = 0; i < N; i++)
        {
            cout << "size=" << veca.size() << endl;
            cout << "capacity=" << veca.capacity() << endl;
            // veca.push_back(A(1));//未提前分配空间,会多次调用构造函数和析构函数
            // veca.emplace_back(A(1));//就地在尾部插入类,省去了拷贝或移动元素的过程。
            veca[i] = A(i);
        }
    }
    cout << "over";
    return 0;
}

19.3 容器的说明和简单应用续

19.3.1 deque和stack

  1. deque
    double-ended queue,双端队列(双向开口)。
    分段数组,内存是分段连续的。
#include 
#include 
using namespace std;

class A
{
public:
    A(int tmp) : m_i(tmp)
    {
        cout << "A(int)" << endl;
    }
    A(const A &tmpA)
    {
        m_i = tmpA.m_i;
        cout << "A(const A&)" << endl;
    }
    ~A()
    {
        cout << "~A()" << endl;
    }

public:
    int m_i;
};

int main()
{
    {
        deque<A> mydeque;
        for (int i = 0; i < 2; i++)
        {
            cout << "begin" << endl;
            mydeque.push_front(A(i));
            mydeque.push_back(A(i));
            cout << "end" << endl;
        }
        for (int i = 0; i < mydeque.size(); i++)
        {
            cout << i << ": " << mydeque[i].m_i << ", address: " << &mydeque[i] << endl;
        }
    }

    cout << "main over" << endl;
    return 0;
}
  1. stack
    栈或堆栈。
    一端开口,后进先出。

19.3.2 queue

队列,先进先出,一端进,一端出。

19.3.3 list

双向链表,内存空间不连续。

沿链查找元素,查找效率不突出;任意位置插入和删除元素都非常迅速。

19.3.4 其它

  1. forward_list
    单向链表,push_front,头部插入数据。无push_back。

  2. map和set
    关联容器,内部实现多为红黑树。
    map容器,每个元素都是键值对(key/value),key不能重复,重复需使用multimap。

#include 
#include 
#include 
using namespace std;

class A
{
public:
    int m_i;
    A(int tmp = 0) : m_i(tmp)
    {
        cout << "A(int)\n";
    }
    A(const A &a)
    {
        m_i = a.m_i;
        cout << "A(const A&) \n";
    }
    ~A()
    {
        cout << "~A()\n";
    }
};

#define N 3
int main()
{
    map<int, string> mymap = {
        pair<int, string>(1, "java"),
        pair<int, string>(2, "c++"),
        {3, "python"},
        make_pair(4, "go")};
    cout << mymap[4] << endl;

    mymap.insert({4, "matlab"}); // key,4存在,这行不执行
    cout << mymap.at(4) << endl;

    mymap[4] = "matlab"; // key,4存在,会被替换
    cout << mymap.at(4) << endl;

    mymap[5] = "ruby"; // key,5不存在,会赋值
    cout << mymap.at(5) << endl;

    cout << mymap.size() << endl;
    cout << mymap[6] << endl;     //生成一个{6, ""}
    cout << mymap.size() << endl; //+1

    auto iter = mymap.find(3);
    if (iter != mymap.end())
    {
        cout << iter->first << endl;
        cout << iter->second << endl;
    }

    cout << "over";
    return 0;
}

set容器中,每个元素都是value,value不能重复,重复需使用multiset。

map、set等关联容器,插入速度慢一些(寻找合适位置),查找速度快。

  1. unordered_map、unordered_set等
    unordered_开头容器属于无序容器,内部一般使用哈希表(散列表)实现。
    unordered_map、unordered_set不重复,unordered_multimap、unordered_multiset可重复。
#include 
#include 
#include 
using namespace std;

int main()
{
    unordered_set<int> myset;
    cout << myset.bucket_count() << endl;

    for (int i = 0; i < 8; i++)
        myset.insert(i);
    cout << myset.bucket_count() << endl;

    cout << myset.max_bucket_count() << endl;
    cout << myset.size() << endl;

    for (int i = 0; i < myset.bucket_count(); i++)
        cout << myset.bucket_size(i) << endl;

    if (myset.find(5) != myset.end())
        cout << "find\n";

    if (find(myset.begin(), myset.end(), 5) != myset.end())
        cout << "find\n";

    cout << "over";
    return 0;
}

19.4 分配器简介、使用与工作原理说

19.4.1 分配器简介

vector<int> myvec;
vector<int, std::allocator<int>> myvec;

内存分配器扮演着内存池的角色,大量减少对malloc的调用以减少对内存分配的浪费。

list<int> mylist;
mylist.push_back(10);
mylist.push_back(20);
mylist.push_back(36);
for(auto iter = mylist.begin(); iter != mylist.end(); ++iter){
	cout<<*iter<<endl;
	int *p=&(*iter);
	printf("%p\n", p);
}
//测试表明,默认分配器未使用内存池工作机制,原样调用底层malloc。

19.4.2 分配器的使用

分配器分配内存,写入数据,释放内存。

allocator<int> aalloc;
int *p=aalloc.allocate(3);
int *q=p;
*q=1;q++;
*q=2;q++;
*q=3;
aalloc.deallocate(p, 3);

19.4.3 其它的分配器与原理说

分配器就是一次分配一大块内存,每次使用一小块,用链表将这些内存块管理起来。通过内存池的内存分配机制,有效减少调用malloc次数,减少内存浪费,提高程序运行效率。

//分配器上不同编号对应不同大小的内存块,
//应对不同大小的内存分配申请
//3个list可能共用一个分配器
list<int, allocator<int>> mylist1;
list<double, allocator<double>> mylist2;
list<int, allocator<int>> mylist3;

19.5 迭代器的概念和分类

19.5.1 迭代器基本概念

可遍历STL容器全部或部分元素的对象(行为类似于指针对象,*iter获取迭代器所指向的内容)。
迭代器由容器提供(容器里面定义者迭代器的具体类型细节),用来表现容器中的某一个位置。

#include 
#include 
using namespace std;

int main()
{
    vector<int> iv = {1, 2, 3};
    for (vector<int>::iterator iter = iv.begin(); iter != iv.end(); ++iter)
    {
        *iter += 6;
        cout << *iter << endl;
    }

    cout << "over";
    return 0;
}

19.5.2 迭代器的分类

依据迭代器的移动特性和操作进行分类。
(1)输出型迭代器(Output iterator)
struct output_iterator_tag
(2)输入型迭代器(Input iterator)
struct input_iterator_tag
(3)前向型迭代器(Input iterator)
struct forward_iterator_tag
(4)双向迭代器(Bidirectional iterator)
struct bidirectional_iterator_tag
(5)随机访问迭代器(Random-access iterator)
struct random_access_iterator_tag

struct output_iterator_tag

继承关系:
struct input_iterator_tag

struct forward_iterator_tag

struct bidirectional_iterator_tag

struct random_access_iterator_tag

多数容器有迭代器,stack、queue等容器不提供迭代器。

迭代器种类 迭代器能力 提供该迭代器的容器
output_iterator_tag 向前写入 ostream
input_iterator_tag 向前读取一次 istream
forward_iterator_tag 向前读取 forward_list、nordered
bidirectional_iterator_tag 向前和向后读取 list、set、multiset、map、multimap
random_access_iterator_tag 随机读取(跳过一定个数的元素) array、vector、deque、string、C风格数组
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

void _display_category(output_iterator_tag mytag)
{
    cout << "output_iterator_tag" << endl;
}
void _display_category(input_iterator_tag mytag)
{
    cout << "input_iterator_tag" << endl;
}
void _display_category(forward_iterator_tag mytag)
{
    cout << "forward_iterator_tag" << endl;
}
void _display_category(bidirectional_iterator_tag mytag)
{
    cout << "bidirectional_iterator_tag" << endl;
}
void _display_category(random_access_iterator_tag mytag)
{
    cout << "random_access_iterator_tag" << endl;
}

template <typename T>
void display_category(T iter)
{
    cout << "begin" << endl;

    typename iterator_traits<T>::iterator_category cagy; //过滤器(萃取机),获取T迭代器类型的种类
    _display_category(cagy);
    cout << "typeid(iter).name()=" << typeid(iter).name() << endl;

    cout << "end" << endl;
}

int main()
{
    display_category(array<int, 100>::iterator()); //()用于产生临时对象
    display_category(vector<int>::iterator());
    display_category(list<int>::iterator());
    display_category(map<int, int>::iterator());
    display_category(set<int>::iterator());

    cout << "over";
    return 0;
}

迭代器功能整理:
(1)输出型迭代器(Output iterator)
struct output_iterator_tag
一步步往前走,通过这个迭代器往容器中写数据。
*iter = value,value写入迭代器指向的位置。
++iter
iter++

(2)输入型迭代器(Input iterator)
struct input_iterator_tag
一步步往前走,通过这个迭代器顺序返回元素值。
*iter,读取元素值
iter->member,读取元素成员
++iter
iter++
iter1 == iter2
iter1 != iter2

(3)前向型迭代器(Input iterator)
struct forward_iterator_tag
继承input_iterator_tag,同时支持写入操作
*iter,读取元素值
iter->member,读取元素成员
*iter = value,value写入迭代器指向的位置。
++iter
iter++
iter1 == iter2
iter1 != iter2
iter1 = iter2,迭代器赋值

(4)双向迭代器(Bidirectional iterator)
struct bidirectional_iterator_tag
继承forward_iterator_tag,增加向回(反向)迭代,即迭代位置可以往回退。
*iter,读取元素值
iter->member,读取元素成员
*iter = value,value写入迭代器指向的位置。
++iter
iter++
iter1 == iter2
iter1 != iter2
–iter
iter–

(5)随机访问迭代器(Random-access iterator)
struct random_access_iterator_tag
继承bidirectional_iterator_tag,增加随机访问能力,即增减偏移量,同时能够计算距离,支持一些关系运算等。
*iter,读取元素值
iter->member,读取元素成员
*iter = value,value写入迭代器指向的位置。
++iter
iter++
iter1 == iter2
iter1 != iter2
–iter
iter–

iter[n]
iter+=n
iter-=n
iter+n
iter-n
iter1-iter2,计算距离
iter1iter2
iter1<=iter2, iter1>=iter2

19.6 算法简介、内部处理与使用范例

19.6.1 算法简介

19.6.2 算法内部一些处理

算法是函数模板,接收各种类型的迭代器作为形参。算法内部根据迭代器类型,有不同处理,处于算法执行效率的考虑。

19.6.3 一些典型算法使用范例

#include 
  1. for_each
void func(int i){
	cout<<i<<endl;
}

vector<int> myvector = {10, 20, 30, 40, 50};
//两个形参迭代器,一个可调用对象,
//不断迭代两个迭代器间的元素,作为实参来调用myfunc函数。
for_each(myvector.begin(), myvector.end(), func);
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f){
	for(; first != last; ++first)
		f(*first);
	return f;
}
  1. find
vector<int> myvector = {10, 20, 30, 40, 50};
vector<int>::iterator finditer = find(myvector.begin(), myvector.end(), 400);
if(finditer != myvector.end())
	cout<<"find"<<endl;
else
	cout<<"not find"<<endl;

//优先使用容器自带(若存在)的同名函数。

map<int, string> mymap;
mymap.insert(std::make_pair(1, "wang"));
mymap.insert(std::make_pair(2, "li"));
auto finditer = mymap.find(2);
if(finditer != mymap.end()){
	cout<<"find"<<endl;
	cout<<iter->first<<iter->second<<endl;
}
  1. find_if
vector<int> myvector = {10, 20, 30, 40, 50};
auto finditer = find_if(myvector.begin(), myvector.end(), [](int val){ //可调用对象
	if(val>15)
		return true; //返回第一个满足规则的元素
	return false;
});
if(finditer != myvector.end())
	cout<<"find"<<*finditer<<endl;
else
	cout<<"not find"<<endl;
  1. sort
vector<int> myvector = {10, 20, 30, 40, 50};
//sort(myvector.begin(), myvector.end());//默认升序
sort(myvector.begin(), myvector.begin()+3);//前三个元素升序
bool myfuncsort(int i, int j){
	//return i
	return i>j;//降序
}
sort(myvector.begin(), myvector.end(), myfuncsort);
class A{
public:
	bool operator()(int i, int j){
		//return i
		return i>j;//降序
	}
};

A a;
sort(myvector.begin(), myvector.end(), a);
list<int> mylist = {10, 20, 30, 40, 50};
//sort(mylist .begin(), mylist .end());//sort算法不适用于双向迭代器,只适用于随机访问迭代器
mylist.sort(myfuncsort);//list容器自带sort成员函数

顺序容器可以排序,关联容器(含无序容器)不适合排序。

map<int, string> mymap;
mymap.insert(make_pair(50, "wang"));
mymap.insert(make_pair(15, "li"));
mymap.insert(pair<int, string>(80, "zhao"));
//sort(mymap.begin(), mymap.end());//error
unordered_set<int> myset = {10, 20, 30, 40, 50};
//sort(myset .begin(), myset .end());//error

19.7 函数对象回顾、系统函数对象与范例

19.7.1 函数对象/仿函数回顾

函数对象(function objects)或仿函数(functors),配合算法使用实现特定功能,调用方式很统一,即:名字(参数列表)。
函数对象形式整理

  • 函数:void func(int x) {…}
  • 可调用对象(类/类模板生成对象):class A{public:void operator()(int x){}};
  • lambda表达式:[](int x){};

19.7.2 标准库中定义的函数对象

#include 
  1. 算术运算类
表达式(函数对象) 效果
negate<类型>() -param
plus<类型>() param1+param2
minus<类型>() param1-param2
multiplies<类型>() param1*param2
divides<类型>() param1/param2
modulus<类型>() param1%param2
  1. 关系运算类
表达式(函数对象) 效果
equal_to<类型>() param1==param2
not_equal_to<类型>() param1!=param2
less<类型>() param1
greater<类型>() param1>param2
less_equal<类型>() param1<=param2
greater_equal<类型>() param1>=param2
  1. 逻辑运算符
表达式(函数对象) 效果
logical_not<类型>() !param
logical_and<类型>() param1&¶m2
logical_or<类型>() param1||param2
  1. 位运算类
表达式(函数对象) 效果
bit_xor<类型>() param1^param2
bit_and<类型>() param1¶m2
bit_or<类型>() param1|param2
template<class _Ty = void>
struct plus{
	constexpr _Ty operator()(const _Ty& _Left, const _Ty& _Right) const {
		return _Left + _Right;
	}
};
cout<<plus<int>()(4, 5)<<endl;
//plus实例化类
//plus()临时可调用对象(重载了operator())
//plus()(4, 5)函数调用

19.7.3 标准库中定义的函数对象范例

vector<int> myvector = {10, 20, 30, 40, 50};
sort(myvector.begin(), myvector.end(), greater<int>());
sort(myvector.begin(), myvector.end(), less<int>());
//greater()和less()产生临时的可调用对象

19.8 适配器概念、分类、范例与总结

19.8.1 适配器基本概念

类似于转接头。将一个既有东西进行适当改造,增减一点东西,就会成为一个适配器。

19.8.2 容器适配器

stack和queue归类为容器适配器,将既有的deque进行适当的改造(阉割一点东西)。

template<class _Ty, class _Container = deque<_Ty>>
class queue {
public:
	//...
	void push(value_type && _Val){
		c.push_back(_STD move(_Val));
	}
protected:
	_Container c;
};

19.8.3 算法适配器

算法是函数模板,算法适配器就是函数适配器,最典型的是绑定器(Binder)。
bind,函数模板,算法适配器中的绑定器。

vector<int> myvector = {50, 15, 80, 30, 46, 80};
int cishu = count(myvector.begin(), myvector.end(), 80);
cout << cishu <<endl;
class A{
public:
	bool operator()(int i){
		return i>40;
	}
};

A a;
cishu = count_if(myvector.begin(), myvector.end(), a);
template<class _Ty = void>
struct less{
	constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const {
		return _Left < _Right;
	}
};

class A{
public:
	bool operator()(int i){
		//return i>40;
		return 40<i;
	}
};

//less()中operator()第一个参数绑定了40
auto bf = bind(less<int>(), 40, placeholders::_1);
bf(19);//调用,第二个参数19

cishu = count_if(myvector.begin(), myvector.end(), bind(less<int>(), 40, placeholders::_1));

19.8.4 迭代器适配器

方向迭代器就是一个迭代器适配器。

vector<int> iv = {100, 200, 300};
for(vector<int>::reverse_iterator riter = iv.rbegin(); riter != iv.rend(); riter++)
	cout<<*riter<<endl;

19.8.5 总结

你可能感兴趣的:(C/C++,学习,整理,c++)