注:原创不易,转载请务必注明原作者和出处,感谢支持!

注:内容来自某培训课程,不一定完全正确!

一 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
}

最新文章

  1. JavaScript基本语法(一)
  2. IIS7.5使用web.config设置伪静态的二种方法(转)
  3. js调用.net后台事件、后台调用前台以及js调用服务器控件
  4. 在非spring组件中注入spring bean
  5. 13.首次安装CY7C68013A驱动失败记(结果竟然是这样)
  6. Maven使用教程
  7. ASP .Net提交时禁用Button
  8. Jplayer小样
  9. iOS Plist 文件的 增 删 改
  10. Shrio认证详解+自定义Realm
  11. (译文)学习ES6非常棒的特性-字符串常量基础
  12. php session 保存到redis 实现session的共享
  13. Linux 总线、设备、驱动模型 与 设备树
  14. abaqus修改inp直接建立工程
  15. 《机器学习实战(基于scikit-learn和TensorFlow)》第六章内容学习心得
  16. [转]phpstorm激活码注册码序列号
  17. 有趣的async
  18. (转载)Unity里实现更换游戏对象材质球
  19. SQL Server 当表分区遇上唯一约束(转载)
  20. golang sqlite3 CRUD

热门文章

  1. ngnix反向代理后获取用户真实ip及https配置
  2. es string 分词完整示例
  3. springboot升级2.0 fastjson报错? 2.0以上应该怎么整合fastjson?
  4. java——OOM内存泄漏
  5. python+selenium之——错误:selenium.common.exceptions.WebDriverException: Message: ‘geckodriver’ executable needs to be in PATH.
  6. Java错误和异常解析
  7. Invalid argument: Key: label. Data types don&#39;t match. Data type: int64 but expected type: float
  8. 使用原生js 实现点击消失效果
  9. Java实现复制文件或者文件夹
  10. crontab踩坑(二):Unit crond.service could not be found.