conncet函数的使用方法

概述

信号和槽机制是 QT 的核心机制,要精通 QT 编程就必须对信号和槽有所了解。信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性,也是 QT 区别于其它工具包的重要地方。信号和槽是 QT 自行定义的一种通信机制,它独立于标准的 C/C++ 语言,因此要正确的处理信号和槽,必须借助一个称为 moc(Meta Object Compiler)的 QT 工具,该工具是一个 C++ 预处理程序,它为高层次的事件处理自动生成所需要的附加代码。

在我们所熟知的很多 GUI 工具包中,窗口小部件 (widget) 都有一个回调函数用于响应它们能触发的每个动作,这个回调函数通常是一个指向某个函数的指针。但是,在 QT 中信号和槽取代了这些凌乱的函数指针,使得我们编写这些通信程序更为简洁明了。 信号和槽能携带任意数量和任意类型的参数,他们是类型完全安全的,不会像回调函数那样产生 core dumps。

所有从 QObject 或其子类 ( 例如 Qwidget) 派生的类都能够包含信号和槽。当对象改变其状态时,信号就由该对象发射 (emit) 出去,这就是对象所要做的全部事情,它不知道另一端是谁在接收这个信号。这就是真正的信息封装,它确保对象被当作一个真正的软件组件来使用。槽用于接收信号,但它们是普通的对象成员函数。一个槽并不知道是否有任何信号与自己相连接。而且,对象并不了解具体的通信机制。

你可以将很多信号与单个的槽进行连接,也可以将单个的信号与很多的槽进行连接,甚至于将一个信号与另外一个信号相连接也是可能的,这时无论第一个信号什么时候发射系统都将立刻发射第二个信号。总之,信号与槽构造了一个强大的部件编程机制。

指针的使用

1.首先利用fatherdialog的默认构造函数(fatherdialog.cpp代码中未给出)即不带参数的构造函数,构造一个名为w的对象. 

2.在点击Child Dialog按钮时,将w作为参数传给带参数的构造函数,构造出一个新的对象,并调用新对象的show函数.

#include "fatherdialog.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    FatherDialog w;//1.调用默认构造函数.
    w.show();

    return a.exec();
}
#include "fatherdialog.h"
#include "ui_fatherdialog.h"
#include <QMessageBox>

FatherDialog::FatherDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::FatherDialog)
{
    ui->setupUi(this);
    QObject::connect(ui->childB,
                     &QPushButton::clicked,
                     this,
                     &FatherDialog::showChildDialog);
}

FatherDialog::~FatherDialog()
{
    delete ui;
}


void FatherDialog::on_pushButton_clicked()
{
    QMessageBox::information(this,"提示","<font size='26'>请告诉我为什么</font>",QMessageBox::Ok);
}

void FatherDialog::showChildDialog()
{
    QDialog * d= new QDialog(this);//2.在已存在的对象中,将自身作为形参传给新的对象,调用有参数的构造函数.
    d->show();
}

自定义类型指针

1.首先使用自定义数据类型typedef 将变量pHello的类型定义为为一个函数指针,指向的函数返回值类型为void,无参数 
2. 定义了一个void函数指针pf,并将其指向Hello 
3.通过指针pf调用函数. 

4.再创建一个pHello类型的变量p(其实就是一个指向函数返回值类型为void,不带参数的函数指针),将其指向Hello,并通过指针调用.

#include <iostream>
using namespace std;

void hello(){
    cout<<"abc"<<endl;
}

int abc(int x){
    return x+1;
}

typedef void (*pHello)();//变量pHello的类型为一个函数指针,指向的返回值类型为void,无参数



int main(int argc, char *argv[])
{
    //int (*fAbc)(int axc);
    //fAbc=&abc;
    //cout<<(*fAbc)(1)<<endl;
    void (*pf)();//定义了一个void函数指针pf
    pf = &hello;//将函数指针指向hello
    //pf = hello;
    (*pf)();//调用函数
    //pf();

    pHello p = &hello;//创造一个void函数指针p指向hello
    (*p)();

    return 0;
}

/*
typedef int (*pF)(int);
pF y;
y=&abc;
cout<<(*y)(1)<<endl;
*/

父类子类的函数指针

1.先通过指向父类的sayHello函数的函数指针,用父类对象调用父类的函数. 
2.子类对象使用指向父类的sayHello函数的函数指针时,因为指针指向的不是父类的虚函数,所以仍然调用的是父类中的同名函数 
3.创建函数指针cp指向父类中的虚函数.第一次通过父类对象调用的是父类中已经实例化的虚函数. 
4.通过子类的对象来调用cp,因为子类中已经对父类的虚函数进行重写,则调用子类中的同名函数. 
总结: 

1.子类对象可以使用父类的函数指针,当指向的函数是虚函数并且在子类中已经有该函数的重写时,调用子类的函数,否则调用父类的函数.

#include <iostream>

using namespace std;

class Person {
    public:
        void sayHello(){
            cout<<"你好"<<"  ";
            printf("%d\n",&Person::sayHello);//输出函数地址
        }
        virtual void sayName(){
            cout<<"我没有名字"<<"  ";
            printf("%d\n",&Person::sayName);
        }
};

class Child : public Person {
    public:
    void sayHello(){
        cout<<"你好"<<"  ";
        printf("%d\n",&Child::sayHello);
    }
    virtual void sayName(){
        cout<<"我是小明"<<"  ";
        printf("%d\n",&Child::sayName);
    }
};

typedef void (*FunctionPointer)();
typedef void (Person::*PersonMemberFunctionPointer)();//PersonMemberFunctionPointer为指向Person类中函数类型为void不带参数的函数的函数指针
typedef void (Child::*ChildMemberFunctionPointer)();//ChildMemberFunctionPointer为指向Child类中函数类型为void不带参数的函数的函数指针

void runfuncName(Person * obj, void (Person::*func)() ){//PersonMemberFunctionPointer func
    (obj ->* func)();
}


int main(int argc, char *argv[])
{
    Person someone;
    Child xiaoming;
    PersonMemberFunctionPointer pp;
    pp = &Person::sayHello;
    (someone .* pp)();//调用someone的sayHello函数
    //等价于 (&someone ->* pp)();
    //也等价于 someone.sayHello();
    (xiaoming.*pp)();//因为pp指向父类的sayHello函数,虽然能够被子类调用但指向的还是父类的函数.
    //pp=&Child::sayHello;(不能运行,需要强制转换)
    ChildMemberFunctionPointer cp = &Child::sayHello;
    (xiaoming.*cp)();//调用子类的函数

    runfuncName(&xiaoming,pp);//调用父类的函数

    PersonMemberFunctionPointer pp2 = &Person::sayName;
    (someone.*pp2)();//调用虚函数本身
    (xiaoming.*pp2)();//必须是公开继承,才有权限
//由于子类中已经实现了虚函数,同时用子类的实例化对象调用所以是子类的已实现的函数

    //pp2 = &Child::sayName;(不能运行,需要强制转换)



    return 0;
}

理解信号

第一步: 
1.click为函数指针 
2. pMember为指向A类的int类型的成员变量的指针 
3. func为指向A类中的返回值为void类型的函数的函数指针 
4. pfunc为指向函数指针的指针 
第二步: 
1.调用runfunName函数,使用成员函数指针调用函数.输出第一条”按A上面的按钮,调用了自己的onClick函数!” 
2.调用conncet函数,将指向函数指针的指针clikc指向A::onClicked;然后通过a.*a.click来调用对象a中的onClicked()函数.输出第二条”按A上面的按钮,调用了自己的onClick函数!” 
3.调用runfunPointer函数,将指向函数指针的指针pfunc指向传进去的函数指针click,通过pfunc调用onClicked()函数.输出第三条”按A上面的按钮,调用了自己的onClick函数!” 
第三步: 
1. 调用connect函数,将指向B类的返回值为void类型的函数的函数指针slot强转为Apointer类型后在赋给signal(signal为指向A类的返回值为void类型的函数的函数指针的指针) 
2.通过A类的实例化对象a来调用B类的onClicked()函数,因为没有确定的B类的实例化对象,所以B类的成员变量x的值为随机值4358424.输出为”按A上面的按钮,调用B的onClicked函数!成员变量x的值为4358424” 
3.通过B类的实例化对象b来调用B类的onClicked()函数, 因为有确定的B类的实例化对象b,所以B类的成员变量x的值为所赋的处置5;输出结果为” 按A上面的按钮,调用B的onClicked函数!成员变量x的值为5”. 
第四步: 
1.调用connect函数,将指向B类的返回值为void类型的函数的函数指针slot强转为Apointer类型后在赋给signal(signal为指向A类的返回值为void类型的函数的函数指针的指针);同时将A类的成员变量—B类对象的指针slotObj指向B的对象b,在调用对象a的函数TreatClickEvent(),使用指针slotObj调用函数,输出结果为” 按A上面的按钮,调用B的onClicked函数!成员变量x的值为5 ” 
总结: 
槽:带有实现的成员函数 
信号:实现为空的成员函数; 
通过conncet函数,来将信号和槽连接起来, 所有从 QObject 或其子类 ( 例如 Qwidget ) 派生的类都能够包含信号和槽。因为信号与槽的连接是通过 QObject 的 connect() 成员函数来实现的。 
connect(sender, SIGNAL(signal), receiver, SLOT(slot)); 
其中 sender 与 receiver 是指向对象的指针,SIGNAL() 与 SLOT() 是转换信号与槽的宏。 
如代码中的void connect(A* a, Signal signal, B* b, Bpointer slot)其中 a为发射对象的指针,signal为信号,b为接收对象的指针. 

除此以外, 一个信号可以连接多个槽,代码中的click信号连接了两个槽.

#include <iostream>
using namespace std;

//第四步才看
class A;
class B;
typedef void (A::*Apointer)();//Apointer为指向A类的返回值为void类型的函数的函数指针
typedef void (B::*Bpointer)();//Bpointer为指向B类的返回值为void类型的函数的函数指针

//第一步开始看
class A {
  public:
    void (A::*click)();//click为函数指针
    void onClicked(){
            cout<<"按A上面的按钮,调用了自己的onClick函数!"<<endl;
        }

    //第四步才看
    B* slotObj;
    void TreatClickEvent(){
        (slotObj->*(Bpointer)click)();
    }
};


//第三步才看
class B {
  public:
    int x=5;
    void onClicked(){
        cout<<"按A上面的按钮,调用了B的onClick函数! 成员变量x的值为"<<x<<endl;
    }
};

//第一步开始看:复习成员变量指针
void runMemberVariblePointer(A * obj, int A::* pMember) {//pMember为指向A类的int类型的成员变量的指针
    cout<<obj->*pMember<<endl;
}
//第一步开始看:复习成员函数指针
void runfuncName(A * obj, void (A::*func)()){//func为指向A类中的返回值为void类型的函数的函数指针.
    (obj->*func)();
}
//第一步看:组合成员变量指针和成员函数指针
void runfuncPointer(A * obj, void (A::*( A::*pfunc ))()){ //Apointer A::* pfunc
    (obj->*(obj->*pfunc))();//pfunc为指向函数指针的指针.
}

//typedef void (A::*Apointer)();
//第二步才看
//typedef void (A::*(A::*Signal))();
typedef Apointer A::* Signal;//signal为指向A类的返回值为void类型的函数的函数指针的指针
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;
    (a.*a.click)();//此时调用的还是A::onClicked();
    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的对象,所以x的值为随机的.
    (b.*(Bpointer)a.click)();//(fb->*(Bpointer)a.click)();此时通过B的确定对象来调用,x的值为5

    //第四步:完善连接A的信号到B的槽
    connect(&a, &A::click,
            fb, &B::onClicked);//将B的是实例化对象赋给A中的B类对象
    a.TreatClickEvent();

    return 0;
}

信号发射和槽的接收

在A类中定义信号,B类中定义槽,调用conncet函数将信号和槽连接后,将信号发散,与之相对应的槽接收信号后,执行操作,输出”接受对象a的信号,运行对象b的函数。

#ifndef A_H
#define A_H

#include <QObject>

class A : public QObject
{
    Q_OBJECT

signals://定义一个信号
    void signal();

public:
    void useSignal(){//使用信号
        emit signal();
    }

};

#endif // A_H
#ifndef B_H
#define B_H

#include <QObject>
#include <iostream>
using namespace std;

class B : public QObject
{
    Q_OBJECT
public:
    void fun() { //定义一个“槽”
        cout<<"接受对象a的信号,运行对象b的函数。"<<endl;
    }
};

#endif // B_H
#include "a.h"
#include "b.h"

int main(int argc, char *argv[])
{
    A a;
    B b;
    //连接信号和槽
    QObject::connect(&a,&A::signal,
                     &b,&B::fun);
    //使用信号
    a.useSignal();
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值