范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

军工物联网技术C模拟实现Qt的信号与槽机制学到牛牛

  作者:学到教育 任金城
  对于大多学习Qt的朋友,心中都有种好奇——那就是Qt最核心的信号与槽是如何实现的,对于小编自己也是一样,当然大家肯定都会去查阅相关资料,但大部分时候也只是一知半解,如果说要自己实现就会又摸不着头脑了;所以小编决定自己亲自用C++实现一个简单版的信号槽,来理解Qt的实现原理。于是小编就在翻阅各牛人朋友的博客和反复研究Qt源码自己重新写了一下以便交流学习。
  我们先还是简单的梳理一下Qt信号与槽的实现机理:在Qt中实现信号与槽最重要的就是通过元对象系统(MOS)的元对象编译器(MOC)将我们定义的需要使用到信号与槽的类中的信号及信号调用槽函数的方法进行定义(这一步就会生成与源文件对应的moc_xx.cpp文件),然后通过系统提供的关联方法(connect)将信号与槽建立一一对应关系,当发射信号(其实就是调用信号函数)时就会通过信号与槽的对应关系找到对应槽函数进行调用。这样的好处就是对于使用者而言不必去关心函数指针回调函数这些对于初学者比较不太容易搞清晰的东西,简化了使用者的操作。当然就像我们在享受幸福生活的时候,就一定有人在我们背后默默付出砥砺前行!这里也一样,对于我们使用者简化了操作,那为了实现这样的效果就需要在后台提供更多的支持。接下来我们就通过代码再来梳理一遍。
  首先我们使用信号与槽肯定就会有信号的发送者与接收者,所以我们就先去定义这两个类对象:
  sender.h
  #pragma once
  #include "object.h"
  class Sender : public Object
  {
  X_OBJECT
  public:
  Sender(int n = 0) : m_num(n){
  }
  void sendSig();
  signals:
  void holdClass(int n);
  int m_num;
  };
  sender.cpp
  #include "sender.h"
  void Sender::sendSig()
  {
  std::cout << "发送信号:holdClass" << std::endl;
  emit holdClass(m_num);
  }
  在Qt中需要使用信号槽的对象都需要直接或间接继承一个类QObject,并且需要添加一个私有宏定义Q_OBJECT,这里就用Object和X_OBJECT代替,signals是Qt中用于声明信号函数的关键字,emit是Qt中用于发送信号定义的关键字,这里我们先假设已经有这些类和宏定义,注意信号函数是不需要我们定义的,他是在MOC预处理生成的moc_xx.cpp中自动生成定义的,所以这里的cpp很简单只有一个普通函数sendSig()的定义。同理我们再自己定义一个信号的接收者对象和其对应的槽函数。
  receiver.h
  #pragma once
  #include "object.h"
  class Receiver : public Object
  {
  X_OBJECT
  public:
  Receiver() {
  }
  public slots:
  void attendClass(int n);
  };
  receiver.cpp
  #include "receiver.h"
  void Receiver::attendClass(int n)
  {
  std::cout << "执行槽函数attendClass:cur class " << n << std::endl;
  }
  这里的slots就是Qt中用于标识槽函数声明的关键字,槽函数是需要用户自己定义的。
  然后我们就需要再将发送者信号与接收者槽关联起来,我们这就提供一个主函数来模拟关联信号与槽,让发送者产生信号:
  main.cpp
  #include "sender.h"
  #include "receiver.h"
  int main()
  {
  Sender xuedao(9527);
  Receiver rjc;
  Object::connect(&xuedao, SIGNAL(holdClass(int)), &rjc, SLOT(attendClass(int)));
  xuedao.sendSig();
  return 0;
  }
  这里的SIGNAL与SLOT在Qt中就是两个转换字符串的宏定义,connect是QObject的一个静态函数方法。
  我们要想这个程序能正常运行起来,接下来我们就需要去定义一个类似QObject的Object类和上面需要用到的关键字与宏定义,以及模拟MOC预处理产生对应的moc_xx.cpp,里面细节的地方为了方便理解我都通过代码注释解释说明了
  object.h
  #pragma once
  #include
  #include
  #include
  #include
  #define signals protected
  #define slots
  #define emit
  #define SLOT(slt) "1"#slt // 1用于标识槽函数
  #define SIGNAL(sig) "2"#sig //2用于标识信号
  class Object;
  struct MetaObject
  {
  //每个对象可能会有多个信号与槽函数,这里就用两个vector分别保存信号与槽函数信息操作起来方便点
  std::vector sigs;
  std::vector slts;
  //activate的功能是通过信号发送者即信号索引找到关联接收者和方法索引并调用对应方法
  static void activate(Object *sender, int idx, void **argv); //void **argv对应信号传递的参数
  struct Connection //用于打包信号接收者与方法的索引(对应上面定义的vector中的信号槽的索引)
  {
  Object *m_receiver;
  int method;
  };
  };
  //Q_OBJECT宏中定义的比较多这里只选择了我们需使用的几个
  //static MetaObject meta用于保存使用该宏定义对象中的信号与槽信号与槽的相关信息
  //getMetaObject()用于返回发送者或接收者对象中的static MetaObject meta对象
  #define X_OBJECT static MetaObject meta;
  virtual MetaObject *getMetaObject();
  virtual void metaCall(int idx, void **argv); //idx为对应槽函数的索引,void**argv用于接收信号传递的参数
  class Object //需要使用信号槽对象的公共接口对象
  {
  X_OBJECT
  public:
  virtual ~Object() {}
  //connect用于建立信号与槽的关联信息
  static void connect(Object *sender, const char *s1, Object *receiver, const char *s2);
  private:
  friend class MetaObject; //用于方便meta对象访问下面的信号槽map
  std::multimap mp; //用于保存信号索引与接收者对象即索引的对应关系
  //由于一个信号可以对应多个槽,同样多个信号也可以对应一个槽,所以这里选用了multimap容器做对应关系映射
  };
  object.cpp
  #include "object.h"
  #include  //调用strcmp函数需要包含
  void MetaObject::activate(Object *sender, int idx, void **argv)
  {
  //在信号槽对应关系的mp中找到发送者idx索引信号对应的接收者及关联方法的调用
  auto ptr = sender->mp.equal_range(idx);
  for(auto it = ptr.first; it != ptr.second; it++) {
  MetaObject::Connection con = it->second;
  con.m_receiver->metaCall(con.method, argv); //调用接收者与发送者信号关联的方法,并传递需要的参数
  }
  }
  void Object::connect(Object *sender, const char *s1, Object *receiver, const char *s2)
  {
  int sig_idx = -1, slt_idx = -1;
  MetaObject *senderMeta = sender->getMetaObject(); //获取发送者中保存的meta对象
  MetaObject *receiverMeta = receiver->getMetaObject(); //获取接收中保存的meta对象
  //比对信号名称找到对应的信号索引
  for(int i = 0; i < senderMeta->sigs.size(); i++) {
  if(0 == strcmp(s1+1, senderMeta->sigs[i].c_str())) {
  sig_idx = i;
  }
  }
  //这里确认是槽函数,并找到对应的槽函数索引
  //如果有信号与信号关联的情况这里就需要去查找接收者对应的信号索引,这里省略了
  if("1" == *s2) {
  for(int i = 0; i < receiverMeta->slts.size(); i++) {
  if(0 == strcmp(s2+1, receiverMeta->slts[i].c_str())) {
  slt_idx = i;
  }
  }
  }
  if(-1 == sig_idx || -1 == slt_idx) {
  std::cout << "no match sig or slt" << std::endl;
  }
  //利用multimap建立信号索引与接收者和方法索引的对应关系
  MetaObject::Connection con = {receiver, slt_idx};
  sender->mp.insert(std::make_pair(sig_idx, con));
  }
  //下面的主要是预留的方便父类调用子类重写方法的接口这里简单定义即可
  void Object::metaCall(int idx, void **ag)
  {
  }
  MetaObject Object::meta;
  MetaObject *Object::getMetaObject()
  {
  return &meta;
  }
  下面就轮到MOC生成的moc_xx.cpp,这些文件在Qt中是自动生成的不需要我们实现,我这里只能手动模拟简单的实现发送者的moc_sender.cpp与接收者的moc_receiver.cpp最终我们编译程序是需要将这两个文件一起编译才能通过的。
  moc_sender.cpp
  #include "sender.h"
  //根据定义的信号槽顺序将信号与槽函数名称进行保存,Qt中会将函数名称参数分开保存处理,这里简单模拟以下就好
  static const char *sigs_name[] = {"holdClass(int)"};
  static const char *slts_name[] = {nullptr}; //空表示当前没有定义对应的函数
  static std::vector sigs(sigs_name, sigs_name+1);
  static std::vector slts;
  MetaObject Sender::meta = {sigs, slts};
  //Sender的信号定义
  void Sender::holdClass(int n)
  {
  void *arg[] = {(void *)&n};
  //调用MetaObject的静态方法activate传递当前的信号发送者对象、信号索引及参数
  MetaObject::activate(this, 0, arg); //0表示当前信号函数在sigs_name[]中的索引
  }
  MetaObject *Sender::getMetaObject()
  {
  return &meta; //返回Sender的meta对象
  }
  void Sender::metaCall(int idx, void **arg)
  {
  // 我们这里Sender 中没有槽函数所以这里没任何操作
  }
  moc_receiver.cpp
  #include "receiver.h"
  static const char *sigs_name[] = {nullptr};
  static const char *slts_name[] = {"attendClass(int)"};
  static std::vector sigs;
  static std::vector slts(slts_name, slts_name+1);
  MetaObject Receiver::meta = {sigs, slts};
  MetaObject *Receiver::getMetaObject()
  {
  return &meta; //返回Receiver的meta对象
  }
  void Receiver::metaCall(int idx, void **arg)
  {
  //这里根据slts_name[]中的索引值调用对应的槽函数
  if(0 == idx) {
  int n = *((int *)arg[0]);
  attendClass(n);
  }
  }
  有了上面这些文件最后我们只需要将所有的.cpp文件一起编译运行就可以实现Qt中信号与槽的效果了:
  g++ object.cpp sender.cpp receiver.cpp moc_sender.cpp moc_receiver.cpp main.cpp -o xuedao
  也可用其他可使用的编译器编译进行编译,这里直接用的g++。
  另外如果某个对象修改或增删了信号或槽就需要去手动修改对应的moc_xx.cpp文件即可,Qt中实现考虑的实际问题会更多,这里只是把整个信号槽关联及调用流程框架进行了梳理,具体的大家可以参考Qt源码做深入学习。

水滴屏配联发科芯片,三星GalaxyA23入门新机曝光,白色版挺好看在安卓高端手机市场一骑绝尘的三星,它是如何做到不依靠国内手机市场成为全球第一的呢?答案很简单,那就是它的走量机型还是要靠其它产品线的。说起三星,不少国内消费者可能最先想到的是Boo支付宝推荐的车险能买吗?可以的。大牌和小牌都有。根据需要自己选择险种。如果你来本地也能找到门路,(一般人也能找到门路,有返点的。)就不必在那上面去买。没有在支付宝上填车牌号的,千万不要再去填了。前几个月我企业设备如何实现信息化管理?每个公司都有各种各样的设备,但关于设备管理总有很多的问题如何将这么多设备有条理地管理起来?如何记录设备的生命周期?如何建立设备档案?下面告诉你如何用简道云解决设备管理难题。第一步统如何看待荣耀公司诉某测评博主云评测诋毁,侵害名誉权案判决,赔偿86698元?云测评的行为,可以说是一种欺骗粉丝,不负责任的行为。作为一名测评博主,未实际测评就直接给出论断谁买谁傻,是极其不负责任的行为。该博主在发布转载的微博时,并未完整客观地呈现所转载的原一加手机真的可以边玩边充吗?电池不会损害吗?包括一加在内的所有安卓机和iPhone,都是可以边玩边充的,不会对电池造成直接的损害,但会间接影响电池的寿命。我们现在使用的智能手机,基本都采用了锂电池,这种电池可以在20到45的两个虎年(19982022)之间的手机使用史公元1998年,那是一个虎年。那年春晚最轰动的一首歌,是王菲与那英合唱的相约九八。多年后,开心麻花的夏洛特烦恼爆红,主角穿越回1998,还向这届春晚和这首歌致敬。那一年,我在北京。苹果iphonese3真机曝光外观设计几乎没变化据此前报道,苹果将于今年3月召开第一场新品发布会,包含iPhoneSE3iPadAir5在内的诸多新品即将发布。今日外媒曝光了小屏手机iPhoneSE3的真机图,新机在外观设计方面华为新规更新,增加多款产品皆可升级,意味着新品还是有缺陷?对于现在手机市场上的消费者来说,因为手机产品的使用寿命大幅度增加,在手机市场上消费者对于手机的存储空间的要求也在逐渐的加大了,尤其是中高端手机市场产品,在现在市场上产品表现参差不齐全球电视销量座次排定前15名中国品牌占8席,小米力压索尼创维去年,中国彩电零售量为3835万台,同比下降13。8,创下近12年来最低。全球彩电市场也受到影响,出货量同比下降6至1。745亿台(奥维睿沃数据),为近六年来最低点。其中,北美欧洲手机在华为专卖店更新系统,丢失近400万的经济数据谁来赔?这么值钱的经济数据,不会存到云端,不会定时转存其它地方,不会更新前保存?就像挑了两框瓷器,使劲往人堆里挤,摔破了怪路人。自己不觉得有照顾好自己财务的义务吗?谁来陪,即便去法院,华为微信收款商业版是怎么回事?如何开通?微信收款商业版是一款可自助开通完全免开发无技术门槛的微信支付官方收款综合经营类产品,主要具有围绕二维码支付收款门店经营管理活动营销等方面的丰富功能,支持顾客使用信用卡支付。微信收款
微信外卖的骗局昨天在刷头条的时候刷到了一个广告,是微信外卖招募代理商的这么一个非常诱人的商机广告,我当时就留下了姓名电话。今天上午的时候就收到了济南总部打来的一个电话,跟我说了这个代理商的佣金收一文带你搞定TCP挥手摘要TCP断开连接TIMEWAITTIMEWAIT优化TCP保活Sokcet编程TCP断开连接TCP断开连接,需要经历四次挥手,通信的双方都可主动断开连接,断开连接通信的双方占用的start与run区别死记硬背1start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码。2通过调用Thread类的start()方法来启动一跨境电商如何应对欧盟新税改电商卖家的销售策略要针对性进行调整例如在亚马逊上销售的商品定价,需要加上增值税,以免降低卖家自己的利润。代扣代缴的政策实行后,货物清关时所缴纳的进口增值税,也无法进行抵扣,需要跟税GooglePhotos的上锁文件夹功能可以在非Pixel手机使用了DoNews12月7日消息(刘文轩)GooglePhotos有一项LockedFolder功能,最初在此前的GoogleIO上公开,并率先提供给Pixel手机用户使用,不过现在这项89岁老教授状告知网获赔70万不讲理!拿我的知识去赚钱文教育研说家知网一家独大,让多少学生和学者无奈但又无可奈何!知网是干什么的呢?想必大家还记得翟天临这个名字,当时有人问他,关于知网的问题。翟天临直接说,知网是什么。作为一个博士生,Go里的nil在日常Golang使用中,你有没有这样的疑惑?nil是什么?哪些可以用nil?哪些不能用nil?接下来,我将对这些内容进行总结。一什么是nil首先nil是一个变量,我们可以在源码包关于SpringCloudAlibaba,看这篇文章就够了!(附教程资料)什么是SpringCloudAlibaba?首先我们需要了解一下SpringCloud,然后再来了解SpringCloudAlibabaSpringCloud源自官方描述Sprin老程序员3万元接的口罩项目,开发周期10天,真香本项目来自程序汪背后的私活小团队,开发了一个口罩项目,给粉丝分享一下解决方案,希望给想接私活的朋友一些经验参考视频版本在B站我是程序汪另一个口罩项目的案例,他是蓝牙直接跟硬件对接,分布式定时任务调度框架实践分布式任务调度框架几乎是每个大型应用必备的工具,本文介绍了任务调度框架使用的需求背景和痛点,对业界普遍使用的开源分布式任务调度框架的使用进行了探究实践,并分析了这几种框架的优劣势和Go语言核心36讲(Go语言实战与应用二十二)学习笔记44使用os包中的API(上)我们今天要讲的是os代码包中的API。这个代码包可以让我们拥有操控计算机操作系统的能力。前导内容os包中的API这个代码包提供的都是平台不相关的API