写在前面:本博客为本人原创,严禁任何形式的转载!本博客只允许放在博客园(.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函数研究的更多相关文章

  1. linux网络编程之IO函数

    Linux操作系统中的IO函数主要有read(),write(),recv(),send(),recvmsg(),sendmsg(),readv(),writev(). 接收数据的recv()函数 # ...

  2. C Socket编程之Connect超时 (转)

    网络编程中socket的分量我想大家都很清楚了,socket也就是套接口,在套接口编程中,提到超时的概念,我们一下子就能想到3个:发送超时,接收超时,以及select超时(注:select函数并不是只 ...

  3. windows编程之Windows Shell 编程

    参考书<VC++ Windows Shell Programming>   这里仅仅是记录下该资源,推荐到下文列出的连接进行查看   用VC++ 进行Windows Shell 扩展编成 ...

  4. Linux下多进程编程之exec函数语法及使用实例

    exec函数族 1)exec函数族说明 fork()函数用于创建一个子进程,该子进程几乎复制了父进程的全部内容,但是,这个新创建的进程如何执行呢?exec函数族就提供了一个在进程中启动另一个程序执行的 ...

  5. (十)Linux 网络编程之ioctl函数

    1.介绍 Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的 ...

  6. C库函数标准编程之fscanf()函数解读及其实验

    函数功能 fscanf()函数用于从参数stream的文件流中读取format格式的内容,然后存放到...所指定的变量中去.字符串以空格或换行符结束(实验1中会对它进一步说明) 函数格式 字符格式说明 ...

  7. python3 第二十一章 - 函数式编程之return函数和闭包

    我们来实现一个可变参数的求和.通常情况下,求和的函数是这样定义的: def calc_sum(*args): ax = 0 for n in args: ax = ax + n return ax 但 ...

  8. 函数式编程之-bind函数

    Bind函数 Bind函数在函数式编程中是如此重要,以至于函数式编程语言会为bind函数设计语法糖.另一个角度Bind函数非常难以理解,几乎很少有人能通过简单的描述说明白bind函数的由来及原理. 这 ...

  9. Linux编程之fork函数

    在Linux中,fork函数的功能就是在一个进程中创建一个新的进程,当前调用fork函数的进程就是产生的新进程的父进程,新进程在以下也称为子进程.在新进程生成之后就会在系统中开始执行. 函数原型:pi ...

随机推荐

  1. python关键字与标识符

    编程语言众多,但每种语言都有相应的关键字,Python 也不例外,它自带了一个 keyword 模块,用于检测关键字. 关键字列表 进入 Python 交互模式,获取关键字列表: >>&g ...

  2. Selenium 切换 Frame

    我们知道网页中有一种节点叫作 iframe ,也就是子 Frame ,相当于页面的子页面,它的结构和外部网页的结构完全一致. Selenium 打开页面后,它默认是在父级 Frame 里面操作,而此时 ...

  3. Kafka版本升级 ( 0.10.0 -> 0.10.2 )

    升级Kafka集群的版本其实很简单,核心步骤只需要4步,但是我们需要在升级的过程中确保每一步操作都不会“打扰”到producer和consumer的正常运转.为此,笔者在本机搭了一个测试环境进行实际的 ...

  4. smarty模板文件书写javascript代码

    在smarty文件里直接写javascript代码时候,造成500错误. javascript代码有很多的{}在同一行,而{}也是smarty引擎解析模板的关键标识符,smarty将对其进行分析,这时 ...

  5. javascript/css压缩工具---yuicompressor使用方法

    1. 下载 地址:https://github.com/yui/yuicompressor/downloads 2. 安装 yuicompressor是由java写成的一组jar文件,需要jdk环境支 ...

  6. Visual Studio for Mac离线安装教程

    Visual Studio for Mac离线安装教程 可以在线安装,也可以离线安装(本次安装博主使用离线,在线安装失败了) 据说翻个墙就可以,有条件的就翻吧 没条件的我于是选择离线安装………… 离线 ...

  7. 重建索引:ALTER INDEX..REBUILD ONLINE vs ALTER INDEX..REBUILD

    什么时候需要重建索引 1. 删除的空间没有重用,导致 索引出现碎片 2. 删除大量的表数据后,空间没有重用,导致 索引"虚高" 3.索引的 clustering_facto 和表不 ...

  8. Redis学习笔记--Redis配置文件Sentinel.conf参数配置详解

    redis-sentinel.conf配置项说明如下: 1.port 26379 sentinel监听端口,默认是26379,可以修改. 2.sentinel monitor <master-n ...

  9. MAC升级node及npm

    清除node.js的cache: sudo npm cache clean -f 安装 n 工具,这个工具是专门用来管理node.js版本的,别怀疑这个工具的名字,是他是他就是他,他的名字就是 &qu ...

  10. Web Service Client使用Microsoft WSE 2.0

    我安装了WSE 2.0 SP3,Setup Type选择Runtime.如果想要让Visual Studio 2005以上版本集成WSE需稍费周折,默认集成Visual Studio 2005. 1. ...