当Qt(C++)中,function成为first-class - 简书

我对C++的使用和理解是不断变化的。从一开始的C with Class 到接触到设计模式,才理解了什么叫真正的OOP;从接触到STL才真正正视Template,了解了什么是GP;从Python和Golang的火热中了解了函数作为first-class的力量;从某些文章对Lisp近乎玄学的推崇中知道了FP的优势和它逐渐在主流的编程方式中兴起的原因。
C++是包容和自由的,自从学了std::function和lambda之后,我也开始逐渐学着向FP方式转变。因为在很多方面,将function作为first-class,对编码带来的不只是形式上的变化,更是思维方式的变化。

lambda与QObject::Connect

C++11lambda表达式和Qt5的搭档,使得可以可以放弃SIGNALSLOT宏,采用一种更加直观和简洁的方式使用Connect。
使用lambda之前,如果要在Qt的main函数里使用slot,不得不另外构造一个类继承QObject,并且定义槽函数,然后才能在main里实例化对象并绑定槽函数:

//myObject.h
#include <QObject>
class myObject : public QObject {
public:myObject(QObject* parent = 0);~myObject();public slots:void onClicked();
};//myObject.cpp
......
void myObject::onClicked(){qDebug() << "clicked";
}//main.cpp
#include <QApplication>
#include <QDebug>
#include <QPushButton>
#include "myObject.h"
int main(int argc, char *argv[]) {QApplication app(argc, argv);QPushButton *button = new QPushButton("click");button->show();myObject *obj = new myObject();QObject::connect(button, SIGNAL(clicked()), obj, SLOT(onClicked()));return app.exec();
}

为了一个槽函数,还要引入另一个类,实在是大动干戈,而且还不直观(这可能也是OOP为人诟病的一个方面吧)。在有了lambda之后,是这么干的:

#include <QDebug>
#include <QPushButton>
int main(int argc, char *argv[]) {QApplication app(argc, argv);QPushButton *button = new QPushButton("click");button->show();QObject::connect(button, &QPushButton::clicked, []() {qDebug() << "clicked";});return app.exec();
}

简洁!一目了然!!

虚函数与std::function

上面的例子其实还有类似的情况,在QWidget上想要自定义鼠标press事件,我们有两种方式:

  • 该widget外部使用eventfilter来拦截该widget的鼠标press事件并处理
  • 自定义继承自QWidget的widget类并覆盖其mousePressEvent()的虚函数

第一种方式不直观,对该控件的处理要到别的地方去寻找,不是很“OOP”;第二种方式和上一例子一样,代价有点大。
其实这样的例子还有很多。传统的OOP在解耦的同时一定会导致体型的臃肿,除此之外还经常会有将处理流程隐藏在层层的封装和继承之中导致的不直观不清晰的问题。
代码说到底是人来写人来读的,任何反直观的都是不好的。编程就像写文章,诘屈聱牙的东西没人愿意看,好的代码一定是读起来酣畅淋漓的。
如果我们自己实现一个Button类,可以是这样的:

//Button.h
class Button {......virtual void onClicked() = 0;
};//MyButton.h
class MyButton : public Button {......void onClicked(){//需要的操作}
};

这样,在我们需要一个Button的时候,新写一个类继承Button,将点击的处理写在onClicked方法内即可。可以,这很“OOP”。
现在呢,我们可以利用std::function,使得函数作为类成员,像对待类的普通成员一样对函数成员进行赋值操作,即可得到我们需要的对象:

//Button.h
class Button {......std::function<void()> _onClicked;
};//在使用的地方
Button btn;
btn._onClicked = [](){//需要的操作
};

喏,更加的简单明了。函数不需要通过继承来特化,而是通过像普通变量一样的方式直接实例化,带来的不光是结构 上的简单,还有语意上的直白。

ScopeGuard

资源的释放从来都是一个问题。文件句柄、锁、等等资源,申请的时候我们可能想着一会儿用完要释放,等到用完之后可能就忘了,或者是因为分支处理漏掉了,这都是很有可能发生的,就算没有在分支处理中漏掉,在各个分支里都重复的写同样的释放资源的代码也很不fashion,没人愿意当CV战士。
利用RAII特性,局部变量析构时候释放资源已经成为一个通用做法。麻烦的是我们需要为各种资源都创建类来利用其析构函数释放资源,太麻烦。现在有了std::function就好了,借鉴一下std::lock_guard,就有了下面的做法:

//ScopeGuard.h
class ScopeGuard {
public:explicit ScopeGuard(std::function<void()> callback): _onExit(callback) {};~ScopeGuard(){_onExit();};private:std::function<void()> _onExit;
};
#define ON_SCOPE_EXIT(callback) ScopeGuard EXIT##__LINE__(callback)//在使用的地方
{HANDLE f = fopen("conf.yaml");ON_SCOPE_EXIT([=](){fclose(f);});       ......{_mutex.lock();ON_SCOPE_EXIT([&](){_mutex.unlock();});......}......
}

资源创建之后,立即跟在后面写释放方式,不会忘不会漏,看起来还明确。
ON_SCOPE_EXIT宏作用在于创建了一个ScopeGuard局部变量;变量名由行号确定,避免了多个ScopeGuard重名的问题。

其它

将function作为first-class带来的改变还有很多,比如将function保存在容器中,将function作为值传递给别的线程执行,返回闭包来创建累加器等等。

作者:sunix
链接:https://www.jianshu.com/p/9a31de8a2450
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

当Qt(C++)中,function成为first-class相关推荐

  1. Qt Quick 中 QML 与 C++ 混合编程详解

    Qt Quick 技术的引入,使得你能够快速构建 UI ,具有动画.各种绚丽效果的 UI 都不在话下.但它不是万能的,也有很多局限性,原来 Qt 的一些技术,比如低阶的网络编程如 QTcpSocket ...

  2. listview qt 选中内容_演练: 操作Qt应用中的QListView

    演练:操作Qt应用中的List 背景 需要针对Qt的ListView组件开发的列表应用进行操作和自动化测试.ListView通常用于含有大量可选项的窗口,比如文件列表.清单等等.以下我们对QListV ...

  3. jQuery中$(function(){})与(function($){})(jQuery)的区别

    首先,这两个函数都是在页面载入后执行的函数,其中两者的区别在于: 在jQuery中$(function(){})等同于jQuery(function(){}),另一个写法为jQuery(documen ...

  4. javascript中 (function(){})();如何理解?

    javascript中 (function(){})();如何理解? javascript中: (function(){})()是匿名函数,主要利用函数内的变量作用域,避免产生全局变量,影响整体页面环 ...

  5. window.onload和jquery中$(function(){ })的区别

    为什么80%的码农都做不了架构师?>>>    今天调试js发现一个问题,我想页面加载完之后才执行我写的js方法,首先我用jqery中$(function(){ })调试了N久都没有 ...

  6. QT解决方案中新建动态链接库工程,且继承于QObject,解决无法生成moc_XXX.cpp文件的问题,解决工程之间的引用问题

    QT解决方案中新建动态链接库工程,且继承于QObject,解决无法生成moc_XXX.cpp文件的问题,解决工程之间的引用问题 参考文章: (1)QT解决方案中新建动态链接库工程,且继承于QObjec ...

  7. Qt/PyQt中使用系统全局的快捷键

    Qt/PyQt中使用系统全局的快捷键 除了全局快捷键部分外,其他的都比较简单,都是我实现"onekeycodehighlighter"中碰到的一些小问题,这里顺面整理一下.事实上, ...

  8. QT发布中遇到的问题 - wufan的专栏 - 博客频道 - CSDN.NET

    QT发布中遇到的问题 - wufan的专栏 - 博客频道 - CSDN.NET QT发布中遇到的问题 分类: qt 2012-04-05 11:15 8人阅读 评论(0) 收藏 举报 最近开始研究qt ...

  9. Qt/E中的键盘设备管理

    转载请注明出处: http://www.cnblogs.com/baizx/ 键盘设备在Qt中表现为QWSKeyboardHandler的一个实例,为了支持各种各样的键盘设备,Qt提供了QWSKeyb ...

最新文章

  1. C# 的快捷键汇总(一)
  2. 2018/8/9 MultiU 6 并查集+dfs,反向建边提高查询效率 !!! / 最大字段和n维(降维)/ 状压+中途相遇法...
  3. datagrid 的标题的内容不对应整齐
  4. ASP.NET Core 3.x启动时运行异步任务(二)
  5. 研究一下识别验证码,。。。随笔记录
  6. linux 桌面使用体验 远程访问win for linux
  7. 有了数据湖探索服务,企业决策“新”中有数
  8. 自动化运维之ansible-安装部署与基础命令篇
  9. 《Web漏洞防护》读书笔记——第9章,XSS防护
  10. 【数据库/数据挖掘/内容检索】 2019年-中国计算机学会推荐国际学术会议和期刊目录(五)
  11. 2021 年推荐免费网络托管免费空间提供商
  12. 曲苑杂坛--收缩数据库日志
  13. 记者求证北京将禁止外地车和外地人员从事网约车传闻
  14. 京东平台小家电用户画像分析报告
  15. Chrome 安装插件 win10 Edge 安装 位置
  16. Js一句话实现打开QQ和客服聊天
  17. centos7网卡开机自动down
  18. ECNA 2017 Problem J: Workout for a Dumbbell 模拟
  19. 知识型IP与网红的区别
  20. esp8266学习感悟

热门文章

  1. [益智类]无敌连连看v 及其操作指南!
  2. 网络安全学术顶会——SP 2023 议题清单、摘要与总结(下)
  3. 重型自卸汽车设计(转向系及前桥设计)(论文+CAD图纸+开题报告+翻译……)
  4. JSS与React的集成
  5. 微信朋友圈怎么做广告推广?
  6. Centos安装可视化桌面(noVNC)
  7. arduino引脚图
  8. 澳门卫星地图 百度卫星地图香港地图全图高清版(含道路、地名标签叠加)
  9. ChatGPT再度封号; 英伟达市值暴涨超2000亿美元
  10. removeAttr() 和 removeProp() 以及 removeClass(类名)和removeClass()的区别