给出一个问题:如果有一个函数f和一个对象x,现在我们希望在x上调用f,而在x的成员函数之外,为了执行这个调用操作,C++提供了3种不同的语法

f(x);//语法#1: f是一个non-member-func
x.f();//语法#2: f是一个member-func,x是一个对象or对象的引用
px->f();//语法#3: f是一个member-func,且px是一个指向对象x的指针

由于存在这3种不同的语法,因此在使用STL的一些组件时,不免会产生一些语法上的冲突。而ptr_func、mem_fun、mem_func_ref这三个函数(在头文件<functional>中)的出现就是用来掩盖C++语言中的一个内在语法不一致的问题!(下面请继续看我写的 test codes 以便于理解该term所要讲述的内容)

test_codes:

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<list>
using namespace std;
class Widget {
public:string m_res;Widget(const string& s):m_res(s){}inline void reviseRes(const string& s) { this->m_res = s; };void testfunc() {if (m_res.length() > 3)reviseRes(string("success!"));//string的长度超过3记作"成功"!else reviseRes(string("failure!"));//string的长度不超过3记作"失败"!}
};
//现在有一个可用于测试Widget对象的非成员函数testvoid test(Widget& w) {if (w.m_res.length() > 3)w.reviseRes(string("success!"));else w.reviseRes(string("failure!"));}
int main(void) {//...return 0;
}

test1:

//语法1:直接传入对象x来调用非成员函数(non-mem-func)
for_each(vw.begin(), vw.end(), test);
//or
for_each(vw.begin(), vw.end(), ptr_fun(test));

test1_result:

test2:

//语法2:使用Widget对象x的成员函数(mem-func)来传入for_each遍历的每一个对象!
//for_each(vw2.begin(), vw2.end(), (&Widget::testfunc));//错误示范!×
for_each(vw2.begin(), vw2.end(), mem_fun_ref(&Widget::testfunc));//正确示范!√

test2_result:

错误示范result:

正确示范result:

test3:

//语法3:使用Widget对象x的成员函数(mem-func)来传入for_each遍历的每一个对象的指针!
//for_each(lpw.begin(), lpw.end(), (&Widget::testfunc));//错误示范1!×
//for_each(lpw.begin(), lpw.end(), mem_fun_ref(&Widget::testfunc));//错误示范2!×
for_each(lpw.begin(), lpw.end(), mem_fun(&Widget::testfunc));//正确示范!√

test3_result:

错误示范1result:

错误示范2result:

正确示范result:

total_test_codes:

#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<list>
using namespace std;
class Widget {
public:string m_res;Widget(const string& s):m_res(s){}inline void reviseRes(const string& s) { this->m_res = s; };void testfunc() {if (m_res.length() > 3)reviseRes(string("success!"));//string的长度超过3记作"成功"!else reviseRes(string("failure!"));//string的长度不超过3记作"失败"!}
};
//现在有一个可用于测试Widget对象的非成员函数testvoid test(Widget& w) {if (w.m_res.length() > 3)w.reviseRes(string("success!"));else w.reviseRes(string("failure!"));}
int main(void) {vector<Widget> vw{ Widget("lzfy"),Widget("tjr") ,Widget("lyfy") };cout << "before using object call test(non-mem-fun):" << endl;for_each(vw.begin(), vw.end(), [](const Widget& w) {cout << w.m_res << "\t";});cout << endl;//语法#1for_each(vw.begin(), vw.end(), (test));//or//for_each(vw.begin(), vw.end(), ptr_fun(test));cout << "after using object call test(non-mem-fun):" << endl;for_each(vw.begin(), vw.end(), [](const Widget& w) {cout << w.m_res << "\t";});cout << endl;cout << "--------------------------------" << endl;//语法#2vector<Widget> vw2{ Widget("lzfy"),Widget("tjr") ,Widget("lyfy") };cout << "before using object call test(mem-fun):" << endl;for_each(vw2.begin(), vw2.end(), [](const Widget& w) {cout << w.m_res << "\t";});cout << endl;for_each(vw2.begin(), vw2.end(), mem_fun_ref(&Widget::testfunc));cout << "after using object call test(mem-fun):" << endl;for_each(vw2.begin(), vw2.end(), [](const Widget& w) {cout << w.m_res << "\t";});cout << endl;cout << "--------------------------------" << endl;//语法#3list<Widget*> lpw;//当然,我这里仅仅用裸指针来do测试而已。实际coding时STL容器要存储指针类型对象时,都要优先使用shared_ptr<T>来存对应之对象指针!lpw.push_back(new Widget("lzfy"));lpw.push_back(new Widget("tjr"));lpw.push_back(new Widget("lyfy"));cout << "before using pointer-to-object call test(mem-fun):" << endl;for_each(lpw.begin(), lpw.end(), [](const Widget* w) {cout << w->m_res << "\t";});cout << endl;for_each(lpw.begin(), lpw.end(), mem_fun(&Widget::testfunc));cout << "after using pointer-to-object call test(mem-fun):" << endl;for_each(lpw.begin(), lpw.end(), [](const Widget* w) {cout << w->m_res << "\t";});cout << endl;return 0;
}

运行结果:

 总结

        本条款意在告诉我们,当

        ①将一个non-member-func(非成员函数)传递给一个STL组件(多数时候是一个STL算法,而你需要传入一个Func/Pred)时,为了避免你为此感到困惑,你要在函数前加一个ptr_fun(do一些类型定义的工作,这个工作细节是干啥的不重要,重要的是你要会用,from Effective STL Term40)

        注:如果你不知道什么时候该使用ptr_fun,什么时候不该使用,那么你大可以再每一次将函数传递给一个STL组件时总是使用它!STL不会在意,且这么做也不会带来代码运行时性能上的损失!最糟糕的仅仅是,当别人阅读你的代码时,如果看到了不必要的ptr_fun,可能会皱眉头(别人很可能会看不懂你为啥要这么干,一头雾水)!即:这么干你的代码可读性会降低,但我个人认为鲁棒性是会提高的!

        ②将一个member-func(成员函数)传递给一个STL组件(多数时候是一个STL算法,而你需要传入一个Func/Pred)时,并且对应之STL容器中存储的是class object(类的对象)时,你要在函数前加一个mem_fun_ref(do一些类型定义的工作,这个工作细节是干啥的不重要,重要的是你要会用,from Effective STL Term40)

        ③将一个member-func(成员函数)传递给一个STL组件(多数时候是一个STL算法,而你需要传入一个Func/Pred)时,并且对应之STL容器中存储的是pointer to class object(指向类对象的指针)时,你要在函数前加一个mem_fun(do一些类型定义的工作,这个工作细节是干啥的不重要,重要的是你要会用,from Effective STL Term40)

        若不按照Term41的3个总结经验写这类代码,则你的代码根本就不可能通过编译!so,老老实实遵守本条款的经验哈~

Term41:理解ptr_func、mem_fun、mem_func_ref的由来相关推荐

  1. Booth理解与Booth改进版的由来

    Booth的理解可以看这篇文章: https://blog.csdn.net/ZHjiao_1997/article/details/52475498 后期的话有时间举个例子 Booth改进版的由来: ...

  2. 【深度学习入门-1】透彻理解卷积的三层含义:从“卷积”、到“图像卷积操作”、再到“卷积神经网络”的含义(学习笔记)

    一.写在前面 笔者在进行卷积神经网络入门的时候花了很多功夫,理解的也不够透彻,基础不牢,地动山摇.在查阅了很多资料后,发现了大佬up"王木头学科学"讲的卷积神经网络的理解,茅塞顿开 ...

  3. 设计模式系列----装饰模式的理解

    装饰模式 提到装饰,我们先来想一下生活中有哪些装饰: 女生的首饰:戒指.耳环.项链等装饰品 家居装饰品:粘钩.镜子.壁画.盆栽等 我们为什么需要这些装饰品呢?很容易想到是为了美,戒指.耳环.项链.壁画 ...

  4. 《如何高效学习》读书笔记(一)——整体性学习策略

    首先奉上思维导图: 比喻的重要性 华罗庚在讲"统筹"方法时,用了"怎样泡茶最省时间"的比喻. 比喻的作用常常被我们忽视,仅仅把它当作文学中的修辞手段,是文学家用 ...

  5. 来吧,我教你画真正的流程图

    什么是流程图? 很多人在接触到产品设计工作的时候都会接触到或者学画这个东西,有些时候受到的教育是这是解释流程用的,并且给了几个案例就让你开始模仿. 其实这个东西真正的名字叫 UML(Unified M ...

  6. 来吧!我教你画真正的流程图

    作者 | 张荣辉 优酷土豆产品总监 什么是流程图? 很多人在接触到产品设计工作的时候都会接触到或者学画这个东西,有些时候受到的教育是这是解释流程用的,并且给了几个案例就让你开始模仿. 其实这个东西真正 ...

  7. 深度学习-超参数和交叉验证

    一. 1.什么是超参数 没接触过机器学习的人可能对这个概念比较模糊.我们可以从两方面来理解 (1)参数值的产生由来 超参数是在开始学习过程之前设置值的参数(人为设置),而不是通过训练得到的参数数据. ...

  8. 高等数学 武忠祥强化班

    武忠祥强化班1:函数 1.单调性 函数导数与函数单调性 1.函数导数大于零=>=>=>函数单调增 --每一点的导数都大于零,每一点的左边小右边大,所以函数单调增 2.函数单调增≠&g ...

  9. 通俗易懂的解释深藏在傅里叶变换背后的奥秘,傅立叶变换的几何意义

    前言 本人非信号专业大佬,写这篇文章只是为了能有更多的"门外汉"入门看似难以亲近的傅立叶变换,或者让更多有兴趣的相关专业学生理解傅里叶变换的思路以及由来. 或者说是讲解普通图像变成 ...

最新文章

  1. 安装Ubuntu时,遇到自定义交换空间swap大小设置问题
  2. liferay namespace用法
  3. java程序解析perl文件_如何从shell脚本执行perl文件
  4. 第十节:基于MVC5+Unity+EF+Log4Net的基础结构搭建
  5. 福大计算机课程表,教学文件 - 福州大学电气工程与自动化学院
  6. mit数据集_MIT的DNN硬件加速器教程(二)流行的DNN和数据集
  7. linux awk浅析(转)
  8. 分别在(ModelAtrs)Ascend、(Ubuntu16.04服务器+18.04镜像)GPU、(Ubuntu18.04)CPU下通过MindSpore实现(cifar10)图像分类
  9. Linux下TCP网络服务器实现源代码3
  10. android widget 开发实例 : 桌面便签程序的实现具体解释和源代码 (上)
  11. Bill Gates推荐,人工智能必读的三本书 -《终极算法》,《超级智能》和《终极发明》zz
  12. webots controller API(C++)
  13. [渝粤教育] 北京师范大学 中国哲学 参考 资料
  14. asp.net单点登录
  15. 打卡签到python代码_[python] 初学python,打卡签到
  16. Altium未连接的网络DRC检查不出的问题
  17. C#实战之CAD二次开发002:绘制直线和绘制圆
  18. 大众点评数据分析报告
  19. Python中各种进制之间的转换
  20. 通过CND方式引入elementui,vue,vuex,vue-router

热门文章

  1. Caffe中的一些概念
  2. 10岁儿子写的两首诗
  3. Worthington胰蛋白酶的物化性质及特异性
  4. oracle 左连接
  5. GaussMind知识图谱解决方案
  6. MFC中的CApp,CMainFrame,CDoc,CView
  7. 爱德曼报告指出,中国的整体信任度高达83,在所有被调查国家中全球排名第一 | 美通社头条...
  8. 中国菜刀使用(图片上传漏洞)
  9. (皮一下)震惊!周星驰居然选修过计算机课程?
  10. ELK6.x—SearchGuard安全加固