Qt 信号与槽的三角恋关系 - 一个很容易就踩的坑

前言

最近在写一个小项目时,遇到了一个自己万万没有想到的一个很深很深的坑,是关于信号与槽的。信号与槽进行了连接,槽函数却没有执行?

简单的坑描述:类A与类B进行信号与槽的绑定,类B与类C进行信号与槽的绑定,最后类C与类A进行信号与槽的绑定;
最后运行发现会报错,于是换种方式绑定;
类A与类B进行信号与槽的绑定,类B与类C进行信号与槽的绑定,最后类A与类C进行信号与槽的绑定;
可以正常运行,却发现类A无法与类C进行信号与槽的绑定。

坑🕳差不多就是这个意思,看不懂没关系,下面用一个小故事展开。
坑🕳关系图:
在这里插入图片描述

故事
过年了,小明到处走访亲戚,领到了一些零花钱。他想用这笔钱去购买玩具车玩,却发现手上的零花钱不够,这该怎么办呢?于是,小明将零花钱交给老妈,老妈拿出一笔钱与小明的零花钱加在一起刚好够买玩具车;于是,老妈将钱交给超市进行购买玩具车;此时问题就来了,超市应该将玩具车交个老妈,还是将玩具车交给小明呢?
正常我们写代码的思路是,为了方便,直接将玩具车交给了小明;但是这样做的话就完全正中踩中了那个坑。
正确的做法应该是,超市将玩具车交给老妈,老妈再将玩具车交给小明,因为超市和小明是没有任何关系的。
故事完!

下面将通过代码方法将过程完整奉献出来!

购买玩具车

小明类 - XiaoMing

ui文件
在这里插入图片描述
通过在输入框中输入小明的零花钱,然后点击去购物按钮,将钱交给老妈。
最后在Label中显示购买的玩具车。

下面只是简单列举相关代码,具体代码请看最下面全部代码处!

  1. 小明的.h文件中定义发射给老妈的信号

    signals:
    	void andMomGoToShopping(double _money);
    
  2. 在构造函数中将小明的信号与老妈的槽函数进行连接

    this->m_pMom = new Mom(66.6);
    // 小明的信号与老妈的槽函数绑定
    connect(this, &XiaoMing::andMomGoToShopping, m_pMom, &Mom::andSonGoShopping);
    
  3. 实现按钮槽函数,并在槽函数中发射信号

    void XiaoMing::on_goShoppingBtn_clicked() {
    	this->xiaoMing_Money = ui.lineEdit->text().toInt();
    
    	// 1.将小明的钱随信号一起发射给老妈
    	emit andMomGoToShopping(this->xiaoMing_Money);
    }
    
  4. 实现购买到玩具车后,在Label处显示

    void XiaoMing::onToyCar(QString _commodity) {
    	QString str = QString("小明买到了%1").arg(_commodity);
    	ui.label->setText(str);
    }
    

老妈类 - Mom

  1. 老妈的.h文件中定义发射给超市的信号

    signals:
    	void andSonGoToShopping(double _money);
    
  2. 构造函数中将信号于超市进行连接

    this->m_pSupermarket = new Supermarket;
    // 老妈的信号与超市的槽函数绑定
    connect(this, &Mom::andSonGoToShopping, m_pSupermarket, &Supermarket::onCommodity);
    
  3. 实现与小明连接的槽函数,并发射信号给超市

    void Mom::andSonGoShopping(double _money) {
    	// 小明的钱和老妈的钱加起来,再去购物
    	double money = this->momMoney + _money;
    
    	// 2. 老妈的钱和小明的钱加起来随信号发射给超市进行购物
    	emit(andSonGoToShopping(money));
    }
    

超市类 - Supermarket

  1. 超市的.h文件中定义发射出去的信号

    signals:
    	void giveCommodity(QString _commodity);
    
  2. 实现与老妈连接的槽函数,并将玩具车随信号发射出去

    void Supermarket::onCommodity(double _money) {
    	// 超市收到了钱,打印提示已购买
    	qDebug() << "花费" << _money << "¥购买了" << this->commodity;
    
    	// 3.超市将玩具车交出去
    	emit giveCommodity(this->commodity);
    }
    

超市交玩具车

好了,到这这里,已经单向将信号与槽连接好了,钱已经交给超市了,那么超市应该将玩具车交给谁呢?老妈?小明?下面我们每种情况都试一下。

情况一:超市将玩具车交给小明

当然,信号与槽的连接是在超市 的构造函数中进行连接!

我们在超市的类中加上小明的头文件,然后在构造函数中进行与小明信号与槽的连接,将玩具车交给小明。

this->m_pXiaoMing = new XiaoMing;
// 超市的信号与小明的槽函数进行连接
connect(this, &Supermarket::giveCommodity, m_pXiaoMing, &XiaoMing::onToyCar);

运行程序:
在这里插入图片描述
显然,这种方式是不行,代码中我仔细的看过了,没有任何的语法上的错误!
出现这种报错我觉得原因是:小明头文件中包含了老妈的头文件,老妈的头文件中包含了超市的头文件,最后超市的头文件包含了小明的头文件;这样就形成了三角循环,显然是有问题的。如下图。
在这里插入图片描述
显示这是无限包含。。。

情况二:超市将玩具车交给小明

当然,信号与槽的连接是在小明 的构造函数中进行连接!

现在换种方式绑定,在小明的构造函数进行超市的信号与小明的槽函数进行绑定。首先将之前的绑定代码注释掉,然后进行下面代码的绑定。

我们在小明的类中加上超市的头文件,然后在构造函数中进行与超市信号与槽的连接,将玩具车交给小明。

this->m_pSupermarket = new Supermarket;
// 超市的信号与小明的槽函数进行绑定
connect(m_pSupermarket, &Supermarket::giveCommodity, this, &XiaoMing::onToyCar);

运行程序:
在这里插入图片描述

发现可以正常运行,但是,输入框中输入钱后单击按钮Label却没有反应;我们在超市类的槽函数中,购买玩具车后会输出一句话:
在这里插入图片描述
可以看到玩具车是已经购买成功的了,该槽函数是已经执行的了。问题就出在了超市与小明信号与槽的连接上!

如下图:
在这里插入图片描述
具体关系就是这样,但是我并不知道问题出在了那里!

其实情况一和情况二都有相同的问题,就是超市都与小明扯上了关系,我觉得这是主要触发问题的地方,如下图:
在这里插入图片描述
正确的做法:超市将玩具车交给老妈,老妈再将玩具车交给小明。
因为再怎么说,玩具车是老妈拿钱去超市买的,超市没理由将玩具车交给小明。

情况三:正确的做法

超市将玩具车交给老妈,老妈再将玩具车交给小明。

将之前错误的操作代码都注释掉。

  1. 在老妈类里加都一个信号,然后再构造函数中,将超市的信号与老妈的信号进行绑定;当超市触发该信号,随之而然的触发老妈的信号。

    signals:
    	void giveCommodity(QString _commodity);
    
    构造函数中的操作:
    // 超市的信号与老妈的信号进绑定
    connect(m_pSupermarket, &Supermarket::giveCommodity, this, &Mom::giveCommodity);
    
  2. 在小明的构造函数中将老妈的信号与小明的槽函数进行连接即可

    // 老妈的信号与小明的槽函数进行绑定
    	connect(m_pMom, &Mom::giveCommodity, this, &XiaoMing::onToyCar);
    

运行程序:
在这里插入图片描述
可以看到已经完全没有问题了,玩具车已经经由老妈之手交到了小明的手上!

下面将奉上全部代码!


全部代码

代码全由VS2017编写!

XiaoMing.h

#pragma once

#include <QtWidgets/QWidget>
#include "ui_XiaoMing.h"

#include "Mom.h"
//#include "Supermarket.h"

class XiaoMing : public QWidget {
	Q_OBJECT

public:
	XiaoMing(QWidget *parent = Q_NULLPTR);

signals:
	void andMomGoToShopping(double _money);

public slots:
	void onToyCar(QString _commodity);


private slots:
	void on_goShoppingBtn_clicked();

private:
	Ui::XiaoMingClass ui;

	double xiaoMing_Money;

	Mom *m_pMom;
	//Supermarket *m_pSupermarket;
};

XiaoMing.cpp

#include "XiaoMing.h"

#pragma execution_character_set("utf-8") // qt支持显示中文

XiaoMing::XiaoMing(QWidget *parent) : QWidget(parent) {
	ui.setupUi(this);

	this->xiaoMing_Money = 0.0;


	this->m_pMom = new Mom(66.6);
	// 小明的信号与老妈的槽函数绑定
	connect(this, &XiaoMing::andMomGoToShopping, m_pMom, &Mom::andSonGoShopping);

	//this->m_pSupermarket = new Supermarket;
	 超市的信号与小明的槽函数进行绑定
	//connect(m_pSupermarket, &Supermarket::giveCommodity, this, &XiaoMing::onToyCar);


	// 老妈的信号与小明的槽函数进行绑定
	connect(m_pMom, &Mom::giveCommodity, this, &XiaoMing::onToyCar);
}



void XiaoMing::on_goShoppingBtn_clicked() {
	this->xiaoMing_Money = ui.lineEdit->text().toInt();

	// 1.将小明的钱随信号一起发射给老妈
	emit andMomGoToShopping(this->xiaoMing_Money);
}


void XiaoMing::onToyCar(QString _commodity) {
	QString str = QString("小明买到了%1").arg(_commodity);
	ui.label->setText(str);
}

Mom.h

#pragma once

#include <qobject.h>

#include "Supermarket.h"

class Mom : public QObject {
	Q_OBJECT

public:
	Mom(double _money, QObject *parent = Q_NULLPTR);

signals:
	void andSonGoToShopping(double _money);
	void giveCommodity(QString _commodity);


public slots:
	void andSonGoShopping(double _money);


private:
	double momMoney;

	Supermarket *m_pSupermarket;
};

Mom.cpp

#include "Mom.h"

Mom::Mom(double _money, QObject *parent) : QObject(parent) {
	this->momMoney = _money;

	this->m_pSupermarket = new Supermarket;

	// 老妈的信号与超市的槽函数绑定
	connect(this, &Mom::andSonGoToShopping, m_pSupermarket, &Supermarket::onCommodity);

	// 超市的信号与老妈的信号进绑定
	connect(m_pSupermarket, &Supermarket::giveCommodity, this, &Mom::giveCommodity);
}

void Mom::andSonGoShopping(double _money) {
	// 小明的钱和老妈的钱加起来,再去购物
	double money = this->momMoney + _money;

	// 2. 老妈的钱和小明的钱加起来随信号发射给超市进行购物
	emit(andSonGoToShopping(money));
}

Supermarket.h

#pragma once
#include <qobject.h>

//#include "XiaoMing.h"

class Supermarket : public QObject {
	Q_OBJECT

public:
	Supermarket(QObject *parent = Q_NULLPTR);


signals:
	void giveCommodity(QString _commodity);

public slots:
	void onCommodity(double _money);

private:
	QString commodity;

	//XiaoMing *m_pXiaoMing;
};

Supermarket.cpp

#include "Supermarket.h"

#include <qdebug.h>
#pragma execution_character_set("utf-8") // qt支持显示中文

Supermarket::Supermarket(QObject *parent) : QObject(parent) {
	this->commodity = "玩具车";

	//this->m_pXiaoMing = new XiaoMing;
	 超市的信号与小明的槽函数进行连接
	//connect(this, &Supermarket::giveCommodity, m_pXiaoMing, &XiaoMing::onToyCar);
}

void Supermarket::onCommodity(double _money) {
	// 超市收到了钱,打印提示已购买
	qDebug() << "花费" << _money << "¥购买了" << this->commodity;

	// 3.超市将玩具车交出去
	emit giveCommodity(this->commodity);
}

例子比较简单,但意义非凡!


总结

这小坑🕳是本人在写小项目时遇到的,花了一天时间将其原因找出来了。虽然不是什么大风大浪,但是也确确实实是我们偶尔会遇到的。

没有必要去看上面那些代码,只需要结合我画的图片,将其思路过程理解就好了,下次你们遇到类似的问题,就知道怎么去解决啦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpp_learners

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值