C++ STL——C++容器的共性和相关概念
注:原创不易,转载请务必注明原作者和出处,感谢支持!
注:内容来自某培训课程,不一定完全正确!
一 STL容器共性机制
STL容器所提供的值都是值(value)寓意,而非引用(reference)寓意,也就是说当我们给容器中插入元素的时候,容器内部实施了拷贝动作,将我们要插入的元素再另行拷贝一份放入到容器中,而不是将原数据元素的引用放入容器中,也就是说我们提供的元素必须能够被拷贝。
(1)除了queue和stack之外,每个容器都提供可返回迭代器的函数,运用返回的迭代器就可以访问元素。
(2)通常STL不会抛出异常,需要使用者传入正确参数
(3)每个容器都提供了一个默认构造函数和默认的拷贝构造函数。
(4)大小相关的方法:size()返回容器中元素的个数,empty()判断容器是否为空
二 STL容器的使用场合
vector | deque | list | set | multiset | map | multimap | |
---|---|---|---|---|---|---|---|
典型内存结构 | 单端数组 | 双端数组 | 双向链表 | 二叉树 | 二叉树 | 二叉树 | 二叉树 |
可随机存取 | 是 | 是 | 否 | 否 | 否 | 对key而言:是 | 否 |
元素搜索速度 | 慢 | 慢 | 非常慢 | 快 | 快 | 对key而言:快 | 对key而言:快 |
元素插入和删除 | 尾端 | 头尾两端 | 任何位置 | - | - | - | - |
(1)vector容器的使用场景:比如软件历史操作记录的存储。
(2)deque的使用场景:比如排队购票系统,对排队者的存储可以采用deque,支持头端的快速移除,尾端的快速添加。
vector和deque的比较:
- vector.at()比deque.at()效率高,比如vector.at(0)是固定的,deque的开始位置却是不固定的。
- 如果有大量的释放操作的话,vector花的时间更少,这和二者的内部实现有关。
- deque支持头部的快速插入和删除,这是deque的优点。
(3)list的使用场景:比如公交乘客的存储,随时可能有乘客下车,支持频繁的不确定位置元素的移除。
(4)set的使用场景:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列。
(5)map的使用场景:比如按ID号存储十万个用户,想要快速通过ID查找对应的用户,二叉树的查找效率就体现出来了。
三 函数对象
重载函数调用操作符的类,其对象常被称为函数对象(function object),即它们是行为类似函数的对象,也叫仿函数(functor),其实就是重载“()”操作符,使得类对象可以像函数那样调用。
注意:
(1)函数对象(仿函数)是一个类,不是一个函数
(2)函数对象重载了“()”操作符使得它可以像函数一样调用
假定某个类有一个重载的operator(),而且重载的operator()要求获取一个参数,我们就将这个类称为“一元仿函数(unary functor)”;相反,如果重载的operator()要求获取两个参数,我们就将这个类称为“二元仿函数(binary functor)”。
下面是函数对象的应用实例。
// 仿函数
class MyPrint
{
public:
MyPrint() { cnt = 0; }
void operator()(int val)
{
cout << val << endl;
++cnt;
}
unsigned getCnt() { return cnt; }
private:
unsigned cnt;
};
void Test1()
{
// 函数对象可以像普通函数那样调用
// 函数对象可以像普通函数那样接受参数
// 函数对象超出了函数的概念,函数对象可以保存函数的调用状态
MyPrint PRINT;
PRINT(10);
// 打印调用次数,使用函数对象可以避免使用全局变量
cout << "调用次数:" << PRINT.getCnt() << endl;
}
void Test2()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
MyPrint PRINT = for_each(v.begin(), v.end(), MyPrint());
cout << "调用次数:" << PRINT.getCnt() << endl;
}
你虽然避免了使用全局变量,但是却要求共用PRINT对象?
四 谓词
谓词是指普通函数或者重载的operator()返回值是bool类型的函数对象(仿函数)。如果operator()接受一个参数,那么叫做一元谓词,如果接受两个参数,那么叫做二元谓词,谓词可以作为一个判断式。
举例:
一元函数对象:for_each
一元谓词:find_if
二元函数对象:transform
二元谓词:sort
五 内建函数对象
STL内建了一些函数对象。分为:
(1)算术类函数对象
(2)关系运算符类函数对象
(3)逻辑运算类函数对象
这些仿函数所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象来履行函数的功能。使用内建函数对象,需要引入头文件#include <functional>
6个算术类函数对象,除了negate是一元运算,其他都是二元运算。
template<class T> T plus<T> // 加
template<class T> T minute<T> // 减
template<class T> T multiplies<T> // 乘
template<class T> T divides<T> // 除
template<class T> T modulus<T> // 取模
template<class T> T negate<T> // 取反
6个关系运算类函数对象,每一种都是二元运算。
template<class T> bool equal_to<T> // 等于
template<class T> bool not_equal_to<T> // 不等于
template<class T> bool greater<T> // 大于
template<class T> bool greater_equal<T> // 大于等于
template<class T> bool less<T> // 小于
template<class T> bool less_equal<T> // 小于等于
逻辑运算类函数对象,not为一元运算,其余为二元运算
template<class T> bool logical_and<T> // 逻辑与
template<class T> bool logical_or<T> // 逻辑或
template<class T> bool logical_not<T> // 逻辑非
六 函数对象适配器
函数对象适配器是完成一些配接工作,这些配接工作包括绑定(bind),否定(negate)以及对一般函数或成员函数的修饰,使其成为函数对象。
bind2st:将参数绑定为函数对象的第一个参数
bind2nd:将参数绑定为函数对象的第二个参数
not1:对一元函数对象取反
not2:对二元函数对象取反
ptr_fun:将普通函数修饰成函数对象
mem_fun:修饰成员函数
mem_fun_ref:修饰成员函数
函数适配器的应用案例。
struct MyPrint : public binary_function<int, int, void>
{
void operator()(int val, int add) const
{
cout << "val = " << val << " add = " << add << " val + add = " << val + add << endl;
}
};
// 仿函数适配器 bind1st bind2nd 绑定适配器
void Test1()
{
vector<int> v;
for (int i = 0; i < 10; ++i)
{
v.push_back(i);
}
// for_each()在位置3只能填入一个参数,如果需要传入多个参数
// 那么你需要用到绑定适配器,绑定适配器的作用是将二元函数对象
// 转变成一元函数对象。
int add = 200;
for_each(v.begin(), v.end(), bind2nd(MyPrint(), add));
// bind1st,将add的值200绑定为第一个参数val
// bind2nd,将add的值200绑定为第二个参数add
}
// 仿函数适配器 not1 not2 取反适配器
struct MyCompare : public binary_function<int, int, bool>
{
// 从大到小排序
bool operator()(int v1, int v2) const
{
return v1 > v2;
}
};
struct MyPrint2
{
void operator()(int v) const
{
cout << v << " ";
}
};
// 大于5
struct MyGreater5 : public unary_function<int, bool>
{
bool operator()(int v) const
{
return v > 5;
}
};
void Test2()
{
vector<int> v;
for (int i = 0; i < 10; ++i)
{
v.push_back(rand() % 100 + 10);
}
for_each(v.begin(), v.end(), MyPrint2());
cout << endl;
sort(v.begin(), v.end(), not2(MyCompare()));
for_each(v.begin(), v.end(), MyPrint2());
cout << endl;
// 如果对二元谓词取反,用not2
// 如果对一元谓词取反,用not1
// 使用not1()将条件由大于5改为小于等于5
vector<int>::iterator ret = find_if(v.begin(), v.end(), not1(MyGreater5()));
if (ret != v.end())
{
cout << *ret << endl;
}
else
{
cout << "没有找到!" << endl;
}
}
// 仿函数适配器 ptr_fun
void MyPrint3(int val, int add)
{
cout << "val = " << val << " add = " << add << endl;
}
void Test3()
{
vector<int> v;
for (int i = 0; i < 10; ++i)
{
v.push_back(i);
}
// 无法直接对函数MyPrint3()进行参数绑定
// for_each(v.begin(), v.end(), MyPrint3);
// 把普通函数适配成函数对象,再进行参数绑定
for_each(v.begin(), v.end(), bind2nd(ptr_fun(MyPrint3), 10));
}
// 成员函数适配器 mem_fun mem_fun_ref
class Person
{
public:
Person(int age, int id) : age(age), id(id) {}
void show()
{
cout << "age = " << age << " id = " << id << endl;
}
public:
int age;
int id;
};
void Test4()
{
// 如果容器中存放的对象或者对象指针,我们for_each算法
// 打印的时候调用类自己提供的打印函数
vector<Person> v;
Person p1(10, 20), p2(30, 40), p3(50, 60), p4(70, 80);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
// 没有提供额外的打印函数,调用Person自己的成员函数show()
// 格式: &类名::函数名
for_each(v.begin(), v.end(), mem_fun_ref(&Person::show));
cout << endl << endl;
// 存储的是对象指针,用mem_fun
vector<Person *> v1;
v1.push_back(&p1);
v1.push_back(&p2);
v1.push_back(&p3);
v1.push_back(&p4);
for_each(v1.begin(), v1.end(), mem_fun(&Person::show));
// 如果存放的是对象,使用mem_fun_ref
// 如果存放的是对象指针,使用mem_fun
}
C++ STL——C++容器的共性和相关概念的更多相关文章
- c++复习:STL之容器
1 STL的string 1 String概念 string是STL的字符串类型,通常用来表示字符串.而在使用string之前,字符串通常是用char*表示的.string与char*都可以用来表示字 ...
- [C++ STL] 各容器简单介绍
什么是STL? 1.STL(Standard Template Library),即标准模板库,是一个高效的C++程序库. 2.包含了诸多常用的基本数据结构和基本算法.为广大C++程序员们提供了一个可 ...
- C++ STL vector容器学习
STL(Standard Template Library)标准模板库是C++最重要的组成部分,它提供了一组表示容器.迭代器.函数对象和算法的模板.其中容器是存储类型相同的数据的结构(如vector, ...
- STL List容器
转载http://www.cnblogs.com/fangyukuan/archive/2010/09/21/1832364.html 各个容器有很多的相似性.先学好一个,其它的就好办了.先从基础开始 ...
- STL之容器适配器queue的实现框架
说明:本文仅供学习交流,转载请标明出处,欢迎转载! 上篇文章STL之容器适配器stack的实现框架已经介绍了STL是怎样借助基础容器实现一种经常使用的数据结构stack (栈),本文介绍下第二种STL ...
- STL的容器算法迭代器的设计理念
1) STL的容器通过类模板技术,实现数据类型和容器模型的分离. 2) STL的迭代器技术实现了遍历容器的统一方法:也为STL的算法提供了统一性. 3) STL的函数对象实现了自定义数据类型的算法运算 ...
- stl之容器、迭代器、算法几者之间的关系
转自:https://blog.csdn.net/bobodem/article/details/49386131 stl包括容器.迭代器和算法: 容器 用于管理一些相关的数据类型.每种容器都有它的优 ...
- STL Queue 容器
STL Queue 容器 Queue简介 queue是队列容器,是一种“先进先出”的容器. queue是简单地装饰deque容器而成为另外的一种容器. # ...
- STL stack 容器
STL stack 容器 Stack简介 stack是堆栈容器,是一种“先进后出”的容器. stack是简单地装饰deque容器而成为另外的一种容器. #include <s ...
随机推荐
- str 文本函数的调用
方法 说明 S.isdigit() 判断字符串中的字符是否全为数字 S.isalpha() 判断字符串是否全为英文字母 S.islower() 判断字符串所有字符是否全为小写英文字母 S.isuppe ...
- 微信小程序开发(四)页面跳转
承接上篇博客. 通过点击按钮跳转到新的页面. 先创建新页面home: 代码如下: // home.js Page({}) // 注册页面 // home.json {} // home.wxml &l ...
- tensorflow模型的保存与恢复,以及ckpt到pb的转化
转自 https://www.cnblogs.com/zerotoinfinity/p/10242849.html 一.模型的保存 使用tensorflow训练模型的过程中,需要适时对模型进行保存,以 ...
- 深度排序模型概述(二)PNN/NFM/AFM
在CTR预估中,为了解决稀疏特征的问题,学者们提出了FM模型来建模特征之间的交互关系.但是FM模型只能表达特征之间两两组合之间的关系,无法建模两个特征之间深层次的关系或者说多个特征之间的交互关系,因此 ...
- Git 分支开发规范
您必须知道的 Git 分支开发规范 Git 是目前最流行的源代码管理工具. 为规范开发,保持代码提交记录以及 git 分支结构清晰,方便后续维护,现规范 git 的相关操作. 分支管理 分支命名 ma ...
- C# 之 .net core -- EF code first连接Mysql数据库
一.在Models 新建两个数据库类 这个是数据库需要生成的类基础(塑造外观) public class User { [Key] public string ID { get; set; } [Ma ...
- 转:SpringBoot 自定义异常@ContollerAdvice ExceptionHandler不起作用
原文链接:https://blog.csdn.net/evanxuhe/article/details/78650979 为了统一异常,我们通常定义一个统一管理所有Exception,包括自定义Exc ...
- [ZJOI2019]语言——树剖+树上差分+线段树合并
原题链接戳这儿 SOLUTION 考虑一种非常\(naive\)的统计方法,就是对于每一个点\(u\),我们维护它能到达的点集\(S_u\),最后答案就是\(\frac{\sum\limits_{i= ...
- Mybatis中的拦截器
作者:moshenglv的专栏 拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法. ...
- 部署lnmp
装包 1.安装依赖包 yum - y install gcc openssl-devel pcre-devel zlib-devel 2.解源码包 .tar.gz 3.切换到解压缩后的目录,配置参数 ...