Windows编程之connect函数研究
写在前面:本博客为本人原创,严禁任何形式的转载!本博客只允许放在博客园(.cnblogs.com),如果您在其他网站看到这篇博文,请通过下面这个唯一的合法链接转到原文!
本博客全网唯一合法URL:http://www.cnblogs.com/acm-icpcer/p/6821921.html
Windows编程之connect函数研究
#include <iostream>
using namespace std; //第四步才看
class A;
class B;
typedef void (A::*Apointer)();
typedef void (B::*Bpointer)(); //第一步开始看
class A {
public:
void (A::*click)();//function pointer for class
void onClicked(){
cout<<"A button,A onClick function"<<endl;
} //第四步才看
B* slotObj;
void TreatClickEvent(){
(slotObj->*(Bpointer)click)();
}
}; //第三步才看
class B {
private:
int x=;
public:
//int x=5;
void onClicked(){
cout<<"A button,B onClick function,x is:"<<x<<endl;
}
}; //第一步开始看:复习成员变量指针
void runMemberVariblePointer(A * obj, int A::* pMember) {
cout<<obj->*pMember<<endl;
}
//第一步开始看:复习成员函数指针
void runfuncName(A * obj, void (A::*func)()){
(obj->*func)();
}
//第一步看:组合成员变量指针和成员函数指针
void runfuncPointer(A * obj, void (A::*( A::*pfunc ))()){ //Apointer A::* pfunc
(obj->*(obj->*pfunc))();
} //typedef void (A::*Apointer)();
//第二步才看
//typedef void (A::*(A::*Signal))();
typedef Apointer A::* Signal;
void connect(A* a, Signal signal, Apointer slot){ //void (A::*slot)()
a->*signal = slot;
}
//第三步才看
void connect(A* a, Signal signal, Bpointer slot){
a->*signal = (Apointer) slot;
}
//第四步才看
void connect(A* a, Signal signal, B* b, Bpointer slot){
a->*signal = (Apointer) slot;
a->slotObj = b;
} int main(int argc, char *argv[])
{
//第一步、理解信号的本质:成员函数指针类型的特殊成员变量
//第二步、连接A本身的信号与槽
A a;
runfuncName(&a,&A::onClicked);
connect(&a,&A::click,&A::onClicked);//a.click = &A::onClicked;
//refers to : connect(A* a, Signal signal, Apointer slot)
(a.*a.click)();
runfuncPointer(&a,&A::click); //第三步:连接A的信号到B的槽
B b; B * fb = &b;
connect(&a, &A::click, &B::onClicked);//a.click = (void (A::*)())&B::onClicked;
(a.*a.click)();
(b.*(Bpointer)a.click)();//(fb->*(Bpointer)a.click)(); //第四步:完善连接A的信号到B的槽
connect(&a, &A::click,
fb, &B::onClicked);
a.TreatClickEvent(); return ;
}
直接看主函数。
主函数的代码简要看分成三个部分:
(1)
第一个部分最开始就创建了A类对象a。
首先做好必要的准备工作,即调用runfuncName函数,通过函数形参我们可以看出这个函数主要是通过传入对象指针及函数指针来调用对象的成员函数(在此处实现了调用a的onClicked函数打印提示信息)。
然后通过connect函数连接a对象中的函数指针click和函数onClicked,那么通过看函数形参我们可以知道这个connect函数的功能就是就是将信号(即函数指针click)和槽(即a对象中的函数onClicked)连接在一起,这样之后,调用click指针寻址后就可以调用a对象中的函数onClicked;效果就像是通过click这个信号触发了onClicked函数。而紧随其后的代码(a.*a.click)();就是起到了检查信号和槽是否连接成功的作用。
那么后面的函数runfuncPointer(&a,&A::click);又有什么作用呢?
把目光转向函数形参:void runfuncPointer(A * obj, void (A::*( A::*pfunc ))())
形参中的A * obj自然指要传一个A类对象的指针进来;而void (A::*( A::*pfunc ))()则表示一个指向函数指针的指针。
而函数体内的语句(obj->*(obj->*pfunc))();则是通过通过这个指向函数指针的指针来找到这个函数指针(即对象a中的信号click),从而通过这个信号触发其对应的槽(即a对象中的函数onClicked)。而为了成功执行这条语句,之前就必须通过函数connect将信号与槽连接起来,否则编译器执行到此处时报错。
那么讲到这里我们就一目了然了:函数runfuncPointer(&a,&A::click);本质上也是用来检查信号和槽是否连接成功的。实际上,这条语句被执行后和代码(a.*a.click)();一样都调用了a对象的函数onClicked,打印出提示信息A button,A onClick function。
(2)
第二个部分与第一个部分的实现目的基本一致,区别在于第二个部分希望通过A类对象的信号去驱动B类信号的成员函数。
故而主要看代码connect(&a, &A::click, &B::onClicked);
这个connect函数的形参为connect(A* a, Signal signal, Bpointer slot),它与第一部分的connect函数的不同之处在于函数体内将A类对象的信号与B类信号的槽连接:a->*signal = (Apointer) slot;,但是明显,A类信号不能直接与B类槽连接,所以在B类槽前面需要写明A类的槽的类型,以此进行强制类型转换。后面的触发信号语句也说明了这一点:
(b.*(Bpointer)a.click)();
其中,加粗语句将A类信号转换为B类信号。这条语句等价于:(fb->*(Bpointer)a.click)();(fb是B类对象的指针)。
(3)
第二部分没能实现访问B类的数据成员,比如语句(a.*a.click)();输出的x的值为随机值。
那么,为了真正的将不同类之间的信号和槽连接在一起,需要重写connect函数:
void connect(A* a, Signal signal, B* b, Bpointer slot){
a->*signal = (Apointer) slot;
a->slotObj = b;
}
connect(&a, &A::click, fb, &B::onClicked);
可以看出,这个connect函数与第二部分的connect函数的最大不同只在于多写了一句a->slotObj = b;,那么这句话将A类对象内部的B类对象指针指向指定的B类对象,以便在A类对象内部通过函数TreatClickEvent触发信号,以真正的连接不同类之间的信号与槽。
这之后,用语句a.TreatClickEvent();在对象中触发信号,便可以真正的访问B类对象的数据成员了。
by TZ@华中农业大学
2017/5/7
Windows编程之connect函数研究的更多相关文章
- linux网络编程之IO函数
Linux操作系统中的IO函数主要有read(),write(),recv(),send(),recvmsg(),sendmsg(),readv(),writev(). 接收数据的recv()函数 # ...
- C Socket编程之Connect超时 (转)
网络编程中socket的分量我想大家都很清楚了,socket也就是套接口,在套接口编程中,提到超时的概念,我们一下子就能想到3个:发送超时,接收超时,以及select超时(注:select函数并不是只 ...
- windows编程之Windows Shell 编程
参考书<VC++ Windows Shell Programming> 这里仅仅是记录下该资源,推荐到下文列出的连接进行查看 用VC++ 进行Windows Shell 扩展编成 ...
- Linux下多进程编程之exec函数语法及使用实例
exec函数族 1)exec函数族说明 fork()函数用于创建一个子进程,该子进程几乎复制了父进程的全部内容,但是,这个新创建的进程如何执行呢?exec函数族就提供了一个在进程中启动另一个程序执行的 ...
- (十)Linux 网络编程之ioctl函数
1.介绍 Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的 ...
- C库函数标准编程之fscanf()函数解读及其实验
函数功能 fscanf()函数用于从参数stream的文件流中读取format格式的内容,然后存放到...所指定的变量中去.字符串以空格或换行符结束(实验1中会对它进一步说明) 函数格式 字符格式说明 ...
- python3 第二十一章 - 函数式编程之return函数和闭包
我们来实现一个可变参数的求和.通常情况下,求和的函数是这样定义的: def calc_sum(*args): ax = 0 for n in args: ax = ax + n return ax 但 ...
- 函数式编程之-bind函数
Bind函数 Bind函数在函数式编程中是如此重要,以至于函数式编程语言会为bind函数设计语法糖.另一个角度Bind函数非常难以理解,几乎很少有人能通过简单的描述说明白bind函数的由来及原理. 这 ...
- Linux编程之fork函数
在Linux中,fork函数的功能就是在一个进程中创建一个新的进程,当前调用fork函数的进程就是产生的新进程的父进程,新进程在以下也称为子进程.在新进程生成之后就会在系统中开始执行. 函数原型:pi ...
随机推荐
- mysql中concat 和 group_concat()的用法
一.CONCAT()函数CONCAT()函数用于将多个字符串连接成一个字符串.使用数据表Info作为示例,其中SELECT id,name FROM info LIMIT 1;的返回结果为+----+ ...
- Linux 日常运维
查看用户信息:w 查看系统负载:uptime 查看系统资源使用情况:vmstat 查看进程动态:top 查看网卡流量:sar 查看网卡流量:nload 查看磁盘读写:iostat 查看磁盘读写:iot ...
- ARM入门最好的文章
一 首先说说arm的发展 可以用一片大好来形容,翻开各个公司的网站,招聘里面嵌入式占据了大半工程师职位.广义的嵌入式无非几种:传统的什么51.avr.pic称做嵌入式微控制器:arm是嵌入式微处理器 ...
- U3D的有限状态机系统
或许广大程序员之前接触过游戏状态机,这已不是个新鲜的词汇了.其重要性我也不必多说了,但今天我要讲到的一个状态机框架或许您以前并未遇到过.所以,我觉得有必要将自己的心得分享一下.下面是一个链接:http ...
- Mac上zip,rar,tar文件命令解压和压缩
经常遇到在windowns上的压缩文件,在mac上解压出现问题,特意总结了下在Terminal里常用命令的方式解压和压缩文件 1.zip压缩文件 zip命令的参数很多,可以利用"zip -- ...
- php技能评测
以下摘抄自:https://www.viphper.com/?p=1236 公司出了一些自我评测的PHP题目,现将题目和答案记录于此,以方便记忆. 1. 魔术函数有哪些,分别在什么时候调用?__con ...
- windows 命令行下 简单好用的查看端口占用情况的方法
在windows命令行窗口下执行: C:\>netstat -aon|findstr "4444" TCP 127.0.0.1:4444 0.0.0.0:0 LISTENIN ...
- ldap 测试表设计
1. ldap_oc_mappings 存储objeckClass 信息 表结构: Column Desc. id objectClass的唯一标识 name objectClass的名称 k ...
- 使用 github Pages 服务建立个人独立博客全过程
你是否有这样子的需求,只是想简单的写写文章,记录下自己的学习心得.成长经历等,都是些文字内容,不需要配置使用数据库.不想购买服务器自己搭建站点,只是想安安静静的用比较舒服的方式来写篇文章. 静态博客就 ...
- 【Spring Boot&&Spring Cloud系列】使用Intellij构建Spring Boot和Mybatis项目
一.创建项目 1.File->New->Project->spring initializer 2.勾选Web SQL Template Engines 3.项目生成之后,点击add ...