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 ...
随机推荐
- iOS 图片剪切和压缩的几个方法
// 图片剪切 - (UIImage*)clipImageWithImage:(UIImage*)image inRect:(CGRect)rect { CGImageRef imageRef ...
- 【RF库Collections测试】Keep In Dictionary
Name:Keep In DictionarySource:Collections <test library>Arguments:[ dictionary | *keys ]Keeps ...
- Jar命令
JAR包是Java中所特有一种压缩文档,其实大家就可以把它理解为.zip包;当然也是有区别的,JAR包中有一个META-INF\MANIFEST.MF文件,当你打成JAR包时,它会自动生成. 一.ja ...
- 《C++标准程序库》笔记之三
本篇博客笔记顺序大体按照<C++标准程序库(第1版)>各章节顺序编排. ---------------------------------------------------------- ...
- react设置多个className
在一个元素上设置样式,有一个固定的样式,然后还有一个使用三元运算符根据条件添加的样式. 比如说有一个固定样式"title": <div className="tit ...
- 微信小程序 禁止ios页面下拉下滑滚动 出现空白的情况
项目需要做了一个图片拖动指定组件上删除,和排序的功能android测试正常, ios会出现拖动图片页面也跟着下滑的尴尬情况. 查文档下拉刷新配置默认是关闭的,后经查找文档发现在本页面page.json ...
- 《Lua程序设计》第4章 语句 学习笔记
Lua中的常规语句包括:赋值.控制结构和过程调用.Lua还支持一些不太常见的语句,如:多重赋值(multiple assignment) 和 局部变量声明.4.1 赋值Lua允许“多重赋值”,也就是一 ...
- Linux平台下mysql的ODBC配置方法
在安装配置之前, 需要先大概了解一下MyODBC的架构. MyODBC体系结构建立在5个组件上,如下图所示: Driver Manager: 负责管理应用程序和驱动程序间的通信, 主要功能包括: 解析 ...
- SqlServer中循环查询结果集
); begin ; open c_test_main;--打开游标 --开始循环 begin fetch next from c_test_main into @id,@value; --赋值到变量 ...
- 修改计算机名或IP后Oracle10g无法启动服务的解决办法
修改计算机名或IP后Oracle10g无法启动服务的解决办法 遇到的问题,问题产生原因不详.症状为,windows服务中有一项oracle服务启动不了,报出如下错误. Windows 不能在 本地计算 ...