C++基础知识(5)STL标准库

1. STL六大模块介绍

1. 容器(container):是一种数据结构,也就是真正用来存储数据的地方。
(1)顺序式容器
(2)关联式容器
(3)无序式容器:无序式容器也是一种关联式容器。
2. 迭代器(iterator):提供了可以访问任何容器的方法。
3. 算法(alogorithm):用来操作容器中的数据的模板函数。
4. 仿函数(functor)
5. 适配器(adaptor)
6. 分配器(allocator)

2. 容器

2.1 顺序式容器

顺序式容器(sequence container):每个元素都有固定的位置,位置取决于插入的时间和地点,与元素的值无关。

  1. vector:将元素置于一个动态数组中,可以随机存储元素。
#include<iostream>
#include<vector>
void testMian()
{
    std::vector<int> ivec{ 1, 2, 3, 4 };
    std::cout << ivec[2] << std::endl; // 使用[]按索引存取
    std::cout << ivec.at(1) << std::endl; // 使用at()函数按索引存取
    /*
    * 两种方式的区别:对于越界时不同
    **/
    std::cout << ivec[10] << std::endl; // 越界,程序直接强制终止
    std::cout << ivec.at(10) << std::endl; // 越界,抛出异常std::out_of_range,我们可进行处理
}

vector特点:数组尾部添加或删除元素非常快,但中间或头部就很麻烦。

std::vector<int> ivec{ 1, 2, 3, 4 };
ivec.push_back(10);
ivec.pop_back();

vector构造函数

#include<vector>
#include<deque>
#include<set>
/*
* 使用初始化列表初始化
**/
std::vector<int> ivec{ 1, 2, 3, 4 };
/*
* 使用复制构造函数初始化
**/
std::vector<int> ivec2(ivec);
/*
* 使用迭代器初始化
**/
std::vector<int> ivec2(ivec.cbegin(), ivec.cend());
std::deque<int> ideque{ 1, 2, 3, 4 };
std::vector<int> ivec3(ideque.cbegin(), ideque.cend());
std::set<int> iset{ 1, 2, 3, 4 };
std::vector<int> ivec3(iset.cbegin(), iset.cend());
/*
* 使用创建长度初始化
**/
std::vector<int> ivec(); // 初始化空的vector
std::vector<int> ivec(10); // 初始化10个元素,初值为0
std::vector<int> ivec(10, 5); // 初始化10个元素,初值为5

vector增加函数

/*
* push_back()函数
**/
ivec.push_back(5); // 在尾部添加,左值形式或右值形式
/*
* insert()函数
* 返回值为指向新添加的那个元素开始的迭代器
**/
auto iter = ivec.insert(ivec.cbegin(), 100); // 在第一个元素前插入100
ivec.insert(ivec.cbegin(), {1, 2, 3, 4}); // 在第一个元素前插入初始化列表
ivec.insert(ivec.cbegin(), 3, 100); // 在第一个元素前插入3个100

vector删除函数

/*
* pop_back()函数
**/
ivec.pop_back(); // 删除最后一个元素
/*
* erase()函数
* 返回值为被删除元素后面那个元素开始的迭代器
**/
auto iter = ivec.erase(ivec.begin()); // 删除第一个元素
ivec.erase(ivec.begin(), ivec.begin() + 3); // 删除一段元素,此处删除前3个元素
// 注意:删除方法为先闭后开,因此上面只删除前3个元素
/*
* clear()函数
**/
ivec.clear(); // 删除容器中所有元素

vector遍历函数

/*
* at()函数
**/
ivec.at(3);
/*
* front()函数
* 返回首元素的引用
**/
int& i = ivec.front();
/*
* back()函数
* 返回尾元素的引用
**/
int& j = ivec.back();
/*
* begin()函数,cbegin()函数
* 返回第一个元素的迭代器
* end()函数,cend()函数
* 返回最后一个元素的下一个元素的迭代器
**/
std::vector<int> ivec{ 1, 2, 3, 4 };
for (auto iter = ivec.cbegin(); iter != ivec.cend(); ++iter)
{
    std::cout << *iter << std::endl; // 输出 1,2,3,4
}
/*
* rbegin()函数,crbegin()函数
* 返回最后一个元素的逆向迭代器迭代器
* rend()函数,crend()函数
* 返回第一个元素的前一个元素的逆向迭代器迭代器
**/
std::vector<int> ivec{ 1, 2, 3, 4 };
for (auto iter = ivec.crbegin(); iter != ivec.crend(); ++iter) 
{
    std::cout << *iter << std::endl; // 输出 4,3,2,1
}

vector判断函数

/*
* empty()函数
* 判断是否为空
**/
c.empty()// 空则返回true(1),否则返回false(0)
/*
* size()函数
* 判断大小的值,返回当前容器中元素的个数
**/
ivec.size(); 
/*
* capacity()函数
* 表示当前容器在不扩容的情况下,最多能容纳多少个元素
**/
ivec.capacity() 
/*
* max_size()函数
* 表示当前机器可以存储的元素数量的最大值
**/
ivec.max_size()

vector其它函数

/*
* swap()函数
* 交换两个同类型容器的数据
**/
ivec2.swap(ivec);
/*
* assign()函数
* 重设vector的值
**/
ivec.assign(XXX); // 重设ivec的元素,因此(XXX)中可以是构造函数的参数
ivec.assign({1, 2, 3});
ivec.assign(ivec.begin(), ivec.begin + 3); // 左闭右开
ivec.assign(3, 100); // 重设3个100
/*
* resize()函数
**/
std::vector<int> ivec{ 1, 2, 3, 4 };
ivec.resize(2); // 重设大小比原来小,现在ivec为{1, 2}
ivec.resize(6); // 重设大小比原来大,现在ivec为{1, 2, 3, 4, 0, 0}
  1. deque:就是双端队列,相比vector,可以很方便在头添加或删除元素。
/*
* 在函数上deque与vector类似,只是增加了头部的操作
**/
ideque.push_front(50);
ideque.pop_front();
  1. list:双向链表,既有向前的指针也有向后的指针。

特点:在任何位置插入和删除元素都比较快,在元素头部操作慢于deque,在元素尾部操作慢于deque和vector。

#include<list>
/*
* 在函数上list不支持 at()函数
* 在函数上增加了对中间元素增删的操作
**/
std::list<int> iList{ 1, 2, 3, 4 };
iList.insert(++iList.begin(), 10); // 在指定位置前插入元素
iList.erase(++iList.begin()); // 移除指定位置的元素
iList.push_back(5);
iList.push_front(0);
  1. string:字符串,在函数上与其它容器有较大区别。
/*
* 函数:const char* c_str() const
* c_str()函数返回的直接就是string对象内部的指针,如果string指向的对象发生变化,返回的对象也会变化
**/
std::string str("Hello World");
std::cout << str.c_str() << std::endl;
/*
* 函数:const char* data() const
* 返回内部指针,只是返回的字符串结尾没有'\0'
**/
std::cout << str.data() << std::endl;
/*
* 函数:compare()
* 重载很多,用的时候查看一下,可以用string对象的任意部分与另一个字符串比较
* 如下,相等输出0,不相等输出-1
**/
str.compare("Hello World");
  1. forward_list:单向链表,就是受限的list,凡是list不支持的forward_list都不支持,而list支持的,forward_list都比list操作效率更高。
    (1)只提供前向迭代器,不支持反向迭代器,即不能iter–,只能iter++
    (2)不支持成员函数size()
    (3)没有指向最末元素的锚点,即不能back()push_back()

2.2 关联式容器

关联式容器(associated container):元素位置取决于元素的值,和插入顺序无关,主要使用红黑树实现。

  1. set/multiset:使用红黑树实现,是一种高度平衡的二叉树。二叉树决定了set的元素存取的值,只与元素本身的值有关。
#include<iostream>
#include<set>
std::set<int> iset{ 13, 50, 6, 8 };
for (int elem : iset)
{
    std::cout << elem << std::endl; // 输出:6,8,13,50
}

set:内部相同数值的元素只能出现一次。
multiset:相同数值的元素可以出现多次。

std::set<int> iset{ 13, 50, 6, 6, 6, 8 };
std::multiset<int> imultiset{ 13, 50, 6, 6, 6, 8 };
for (int elem : iset)
{
    std::cout << elem << std::endl; // 输出:6,8,13,50
}
for (int elem : imultiset)
{
    std::cout << elem << std::endl; // 输出:6,6,6,8,13,50
}
  1. map/multimap:使用红黑树实现,是一种高度平衡的二叉树。与set不同,存储的是键值对,内部元素根据其键自动排序。
    map:相同的键只能出现一次。
    multimap:相同的键可以出现多次。
std::map<int, std::string> int_string_map;
int_string_map.insert(std::pair<int, std::string>(10, "10"));
int_string_map.insert(std::pair<int, std::string>(1, "1"));
int_string_map.insert(std::pair<int, std::string>(66, "66"));
for (auto& elem : int_string_map)
{
    std::cout << elem.first << "/" << elem.second << std::endl; // 根据键输出:1,10,66
}

2.3 无序式容器

无序式容器(unordered container):主要使用哈希表实现。

  1. unordered_map/unordered_multimap:利用哈希表的特性,实现了无序,使用方法与map/multimap类似。
  2. unordered_set/unordered_multiset:使用方法与set/multiset类似。

2.4 关联式容器与无序式容器

关联式容器与无序式容器的区别:
(1)关联式容器是按值有序的,所以在对于增加元素、删除元素方面的效率比无序式容器高很多。
(2)无序式容器式是哈希表实现的,所以在修改特定元素、查找元素方面的效率比关联式容器高很多。
(3)从内存消耗来讲,无序式容器要高于关联式容器。
总结:因此,增加元素、删除元素使用关联式容器,修改元素、查找元素使用无序式容器。

2.5 容器的选择

(1)在我们需要存储key/value的数据时,只能使用map类容器,同时比较增加删除频繁还是修改查找频繁。
(2)在处理普通元素时,当元素需要频繁插入时,使用顺序容器。当元素需要频繁查找时选set类容器。
(3)在真正项目中,会对各种容器进行测试,练习时掌握vectorset即可。

3. 迭代器

迭代器提供了一种可依顺序访问容器中各个元素的方式,可以让我们无视容器存储的不同,而使用同一方法访问数据。
迭代器的作用:使用迭代器可以灵活操作各种容器算法,而不需要考虑不同容器之间的差异。

4. 算法

算法详细文档微软 algorithm(STL)库参考
STL的算法分为几个种类:
(1)查找算法
(2)排序算法
(3)删除和替换算法
(4)排列组合算法
(5)算数算法
(6)生成和异变算法
(7)关系算法
(8)集合算法
(9)堆算法

5. 仿函数

仿函数:就是一个可以调用“()”运算符的类对象,使用operator()将()运算符重载了的类对象就是仿函数。
仿函数详细文档微软 functional(STL)库参考
此处只介绍常用的仿函数
(1)plusminusmultipliesdivides(加,减,乘,除)

std::plus<int> iPlus;
iPlus(1, 3);

(2)greater(大于比较),less(小于比较),equal_to(等于比较)

/*
* 使用lambda表达式进行从大到小
**/
std::vector<int> ivec{ 5, 3, 7, 10, 2, 13 };
std::sort(ivec.begin(), ivec.end(), [](int elem1, int elem2) {
    return elem1 > elem2;
    }
);
/*
* 使用STL自带的仿函数进行从大到小
**/
std::vector<int> ivec{ 5, 3, 7, 10, 2, 13 };
std::sort(ivec.begin(), ivec.end(), std::greater<int>());

(3)logical_and(逻辑与),logical_or(逻辑或),logical_not(逻辑非)

6. 适配器与分配器

6.1 适配器

C++标准库,提供了三种顺序容器适配器,分别是stackqueuepriority_queue
stack:栈,先进后出,底层使用deque实现。
queue:队列,先进先出,底层使用deque实现。
priority_queue:优先级队列,不同于队列,排序基于优先级排序(堆排序),并只有一个头部指针top,底层使用vector实现。

6.2 分配器

在分配动态内存时,直接使用newdelete容易产生内存碎片化的问题,不同的分配器有不同分配内存的方法,可以极大提高程序对堆内存的使用效率。
new vector构造函数要指定分配器,就算不加分配器参数,也有默认的分配器参数,我们使用默认分配器就好。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值