首页 Qt 学习之路 2 Qt 学习之路 2(5):自定义信号槽

Qt 学习之路 2(5):自定义信号槽

144 12.9K

上一节我们详细分析了connect()函数。使用connect()可以让我们连接系统提供的信号和槽。但是,Qt 的信号槽机制并不仅仅是使用系统提供的那部分,还会允许我们自己设计自己的信号和槽。这也是 Qt 框架的设计思路之一,用于我们设计解耦的程序。本节将讲解如何在自己的程序中自定义信号槽。

信号槽不是 GUI 模块提供的,而是 Qt 核心特性之一。因此,我们可以在普通的控制台程序使用信号槽。

经典的观察者模式在讲解举例的时候通常会举报纸和订阅者的例子。有一个报纸类Newspaper,有一个订阅者类SubscriberSubscriber可以订阅Newspaper。这样,当Newspaper有了新的内容的时候,Subscriber可以立即得到通知。在这个例子中,观察者是Subscriber,被观察者是Newspaper。在经典的实现代码中,观察者会将自身注册到被观察者的一个容器中(比如subscriber.registerTo(newspaper))。被观察者发生了任何变化的时候,会主动遍历这个容器,依次通知各个观察者(newspaper.notifyAllSubscribers())。

下面我们看看使用 Qt 的信号槽,如何实现上述观察者模式。注意,这里我们仅仅是使用这个案例,我们的代码并不是去实现一个经典的观察者模式。也就是说,我们使用 Qt 的信号槽机制来获得同样的效果。

//!!! Qt5
#include <QObject>

////////// newspaper.h
class Newspaper : public QObject
{
    Q_OBJECT
public:
    Newspaper(const QString & name) :
        m_name(name)
    {
    }

    void send()
    {
        emit newPaper(m_name);
    }

signals:
    void newPaper(const QString &name);

private:
    QString m_name;
};

////////// reader.h
#include <QObject>
#include <QDebug>

class Reader : public QObject
{
    Q_OBJECT
public:
    Reader() {}

    void receiveNewspaper(const QString & name)
    {
        qDebug() << "Receives Newspaper: " << name;
    }
};

////////// main.cpp
#include <QCoreApplication>

#include "newspaper.h"
#include "reader.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    Newspaper newspaper("Newspaper A");
    Reader reader;
    QObject::connect(&newspaper, &Newspaper::newPaper,
                     &reader,    &Reader::receiveNewspaper);
    newspaper.send();

    return app.exec();
}

当我们运行上面的程序时,会看到终端输出 Receives Newspaper: Newspaper A 这样的字样。

下面我们来分析下自定义信号槽的代码。

这段代码放在了三个文件,分别是 newspaper.h,reader.h 和 main.cpp。为了减少文件数量,可以把 newspaper.h 和 reader.h 都放在 main.cpp 的main()函数之前吗?答案是,可以,但是需要有额外的操作。具体问题,我们在下面会详细说明。

首先看Newspaper这个类。这个类继承了QObject类。只有继承了QObject类的类,才具有信号槽的能力。所以,为了使用信号槽,必须继承QObject。凡是QObject类(不管是直接子类还是间接子类),都应该在第一行代码写上Q_OBJECT。不管是不是使用信号槽,都应该添加这个宏。这个宏的展开将为我们的类提供信号槽机制、国际化机制以及 Qt 提供的不基于 C++ RTTI 的反射能力。因此,如果你觉得你的类不需要使用信号槽,就不添加这个宏,就是错误的。其它很多操作都会依赖于这个宏。注意,这个宏将由 moc(我们会在后面章节中介绍 moc。这里你可以将其理解为一种预处理器,是比 C++ 预处理器更早执行的预处理器。) 做特殊处理,不仅仅是宏展开这么简单。moc 会读取标记了 Q_OBJECT 的头文件,生成以 moc_ 为前缀的文件,比如 newspaper.h 将生成 moc_newspaper.cpp。你可以到构建目录查看这个文件,看看到底增加了什么内容。注意,由于 moc 只处理头文件中的标记了Q_OBJECT的类声明,不会处理 cpp 文件中的类似声明。因此,如果我们的NewspaperReader类位于 main.cpp 中,是无法得到 moc 的处理的。解决方法是,我们手动调用 moc 工具处理 main.cpp,并且将 main.cpp 中的#include "newspaper.h"改为#include "moc_newspaper.h"就可以了。不过,这是相当繁琐的步骤,为了避免这样修改,我们还是将其放在头文件中。许多初学者会遇到莫名其妙的错误,一加上Q_OBJECT就出错,很大一部分是因为没有注意到这个宏应该放在头文件中。

Newspaper类的 public 和 private 代码块都比较简单,只不过它新加了一个 signals。signals 块所列出的,就是该类的信号。信号就是一个个的函数名,返回值是 void(因为无法获得信号的返回值,所以也就无需返回任何值),参数是该类需要让外界知道的数据。信号作为函数名,不需要在 cpp 函数中添加任何实现(我们曾经说过,Qt 程序能够使用普通的 make 进行编译。没有实现的函数名怎么会通过编译?原因还是在 moc,moc 会帮我们实现信号函数所需要的函数体,所以说,moc 并不是单纯的将 Q_OBJECT 展开,而是做了很多额外的操作)

Newspaper类的send()函数比较简单,只有一个语句emit newPaper(m_name);。emit 是 Qt 对 C++ 的扩展,是一个关键字(其实也是一个宏)。emit 的含义是发出,也就是发出newPaper()信号。感兴趣的接收者会关注这个信号,可能还需要知道是哪份报纸发出的信号?所以,我们将实际的报纸名字m_name当做参数传给这个信号。当接收者连接这个信号时,就可以通过槽函数获得实际值。这样就完成了数据从发出者到接收者的一个转移。

Reader类更简单。因为这个类需要接受信号,所以我们将其继承了QObject,并且添加了Q_OBJECT宏。后面则是默认构造函数和一个普通的成员函数。Qt 5 中,任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数。与信号函数不同,槽函数必须自己完成实现代码。槽函数就是普通的成员函数,因此作为成员函数,也会受到 public、private 等访问控制符的影响。(我们没有说信号也会受此影响,事实上,如果信号是 private 的,这个信号就不能在类的外面连接,也就没有任何意义。)

main()函数中,我们首先创建了NewspaperReader两个对象,然后使用QObject::connect()函数。这个函数我们上一节已经详细介绍过,这里应该能够看出这个连接的含义。然后我们调用Newspapersend()函数。这个函数只有一个语句:发出信号。由于我们的连接,当这个信号发出时,自动调用 reader 的槽函数,打印出语句。

这样我们的示例程序讲解完毕。我们基于 Qt 的信号槽机制,不需要观察者的容器,不需要注册对象,就实现了观察者模式。

下面总结一下自定义信号槽需要注意的事项:

  • 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
  • 使用 signals 标记信号函数,信号是一个函数声明,返回 void,不需要实现函数代码;
  • 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
  • 使用 emit 在恰当的位置发送信号;
  • 使用QObject::connect()函数连接信号和槽。

Qt 4

下面给出 Qt 4 中相应的代码:

//!!! Qt4
#include <QObject>

////////// newspaper.h
class Newspaper : public QObject
{
    Q_OBJECT
public:
    Newspaper(const QString & name) :
        m_name(name)
    {
    }

    void send() const
    {
        emit newPaper(m_name);
    }

signals:
    void newPaper(const QString &name) const;

private:
    QString m_name;
};

////////// reader.h
#include <QObject>
#include <QDebug>

class Reader : public QObject
{
    Q_OBJECT
public:
    Reader() {}

public slots:
    void receiveNewspaper(const QString & name) const
    {
        qDebug() << "Receives Newspaper: " << name;
    }
};

////////// main.cpp
#include <QCoreApplication>

#include "newspaper.h"
#include "reader.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    Newspaper newspaper("Newspaper A");
    Reader reader;
    QObject::connect(&newspaper, SIGNAL(newPaper(QString)),
                     &reader,    SLOT(receiveNewspaper(QString)));
    newspaper.send();

    return app.exec();
}

注意下 Qt 4 与 Qt 5 的区别。

Newspaper类没有什么区别。

Reader类,receiveNewspaper()函数放在了 public slots 块中。在 Qt 4 中,槽函数必须放在由 slots 修饰的代码块中,并且要使用访问控制符进行访问控制。其原则同其它函数一样:默认是 private 的,如果要在外部访问,就应该是 public slots;如果只需要在子类访问,就应该是 protected slots。

main()函数中,QObject::connect()函数,第二、第四个参数需要使用SIGNALSLOT这两个宏转换成字符串(具体事宜我们在上一节介绍过)。注意SIGNALSLOT的宏参数并不是取函数指针,而是除去返回值的函数声明,并且 const 这种参数修饰符是忽略不计的。

下面说明另外一点,我们提到了“槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响”,public、private 这些修饰符是供编译器在编译期检查的,因此其影响在于编译期。对于 Qt4 的信号槽连接语法,其连接是在运行时完成的,因此即便是 private 的槽函数也是可以作为槽进行连接的。但是,如果你使用了 Qt5 的新语法,新语法提供了编译期检查(取函数指针),因此取 private 函数的指针是不能通过编译的。

144 评论

Anonymous 2012年8月25日 - 13:06

下面给出 Qt 4 中【响应】的代码:

应该是【相应】吧

回复
DevBean 2012年8月25日 - 13:49

已经修改过来,感谢指出!

回复
zqj127 2015年5月22日 - 14:35

感谢分享~

回复
DevBean 2012年10月24日 - 10:56

感谢指出,已经修改过了!

回复
wxf 2012年10月23日 - 20:21

Qt4的代码newspaper.h的信号应为 void newPaper(const QString &name) const ; 少了一个const
错误信息:passing 'const Newspaper' as 'this' argument of 'void Newspaper::newPaper(const QString&)' discards qualifiers

btw,有错别字 “所谓,为了使用信号槽,必须继承 QObject”应该是“所以”吧。

ps:感谢豆子

回复
斯啦丝拉 2012年11月6日 - 12:18

我使用的是Ubuntu+Qt5Beta1
QObject::connect函数貌似不接受const成员函数指针
另外,const函数不能调用非const成员函数
以下函数的声明需要修改
in class Newspaper:
void send() const; -> void send();
in class Reader:
void receiveNewspaper(cosnt QString & name) const; -> void receiveNewspaper(const QString &name);

回复
DevBean 2012年11月6日 - 14:44

已经修改过了,多谢指出!

回复
adfae 2012年11月23日 - 20:51

qt4 代码里有两处笔误:

void receiveNewspaper(cosnt QString & name) 这里的cosnt...字母被调换了

void newPaper(const QString &name); 这个,后面忘记加 const了吧.
void newPaper(const QString &name) const;

回复
DevBean 2012年11月24日 - 12:08

已经修改过了,谢谢指出!

回复
smile 2013年1月7日 - 18:32

Qt5的代码在我这编译不了,环境:Win7(32bit)+QtCreator+qt5_msvc2010
总是报Lnk2001的错误,不知为何?
编译截图在这里:

回复
smile 2013年1月7日 - 18:33 回复
豆子 2013年1月7日 - 19:30

这个错误一般是少加了 Q_OBJECT 宏,如果你在之后添加的,要先运行下 qmake 重新生成 makefile 才可以的

回复
smile 2013年1月7日 - 21:52

我直接复制你的代码,应该不会有问题啊

回复
smile 2013年1月7日 - 23:22

换了一个开发环境:
Ubuntu12.04 (32bit)+ Qt5
同样复制代码,测试通过。可能是我windows的vc2010一些设置有问题吧。

回复
Fantasy 2017年2月10日 - 16:22

看评论很重要,构建前先运行一下qmake,觉得可以在前面的章节对qmake进行简单介绍,毕竟挺重要的

回复
smile 2013年1月9日 - 11:08

找到解决办法了,贴在这里,为以后碰到同样问题的兄弟省点时间:
先qmake一下再构建项目就可以了。
具体可以参考这里:http://stackoverflow.com/questions/8247404/why-does-this-simple-qt-application-not-link

回复
豆子 2013年1月9日 - 14:12

有时候就是因为没有重新运行 qmake,导致 makefile 版本较旧。一般先尝试重新运行 qmake 再编译看看。

回复
sailor 2014年8月19日 - 19:43

是的,只要qmake一下就好。谢谢

回复
李小辉爱学习 2014年10月4日 - 15:30

看了好久不知道如何qmake一下

回复
豆子 2014年10月4日 - 16:35

如果使用 Qt Creator,就在项目上面点右键,有运行 qmake 的选项。

Fantasy 2017年2月10日 - 16:23

菜单栏有一个`构建(B)`,点击`执行qmake`

hechaoyuyu 2013年4月19日 - 23:53

文中一处笔误"比如 newspaper.h 将生成 moc_newspaper.h",应该是生成moc_newspaper.cpp。

回复
豆子 2013年4月20日 - 13:58

感谢指出哦!

回复
asura 2013年5月4日 - 16:29

最近在学习Qt,感谢作者精彩的教程!
问一个可能非常粗浅的问题:
这句“Newspaper(const QString & name):m_name(name)”
应该是个构造函数,那“:m_name(name)”是什么意思?

回复
豆子 2013年5月5日 - 17:20

这个是 C++ 语法,叫做“成员初始化列表”,例如 m_name(name) 作用相当于在构造函数中添加这么一句:m_name = name。但是,初始化列表的执行是在构造函数的实际语句之前进行的。如果对此不理解,需要重新看看 C++ 基础了哦~

回复
asura 2013年5月5日 - 18:16

在后面的章节,也有人问,看到解答了,没见过这种初始化的方式,回去翻了下《C++ primer》,还真有:shock:

回复
轨迹 2013年5月10日 - 09:26

写得真好,想通过qt平台做点事情和学习一下软件设计

回复
豆子 2013年5月12日 - 21:28

嘿嘿,过奖过奖

回复
轨迹 2013年5月28日 - 10:07

槽函数就是普通的成员函数,因此也会受到 public、private 等访问控制符的影响。
博主,是怎么的影响呢?我好想发现把slots声明为private的,没有影响。
同时我把slots函数声明为虚函数(此时为private的)。派生出新的类DReader,在派生类没有实现虚函数。
在connect的时候,接受的对象是DReader的对象,代码同样正确运行的。
不是很清楚这个slots是public,private是否有什么影响。

回复
豆子 2013年5月28日 - 10:31

首先说明,public、private 这样的访问修饰符是供编译器检查的,对于运行期连接的信号槽,不会受到这些修饰符的影响。也就是说,槽函数作为成员函数,会受到 public、private 等访问控制符的影响;作为槽,始终是 public 的。另外需要说明一点,由于 Qt5 的新语法提供了编译期检查,因此 private 的槽函数在 Qt5 的新语法情况下是无法通过编译的。

回复
bestheart 2014年3月13日 - 10:16

很感谢豆子的分享。。。。
“作为槽,始终是 public 的”的说法是否有误?
如果connect()是成员函数的话,槽好像可以为private的。
例如第8章,添加动作里的槽open()。

回复
豆子 2014年3月17日 - 09:08

这里说的是,当这个函数作为槽使用,也就是在 connect() 函数中使用的时候,用 Qt 4 的语法,会无视 private 限定符的(意思是,你可以在 connect() 函数中将信号与 private 的槽函数连接起来,这不会报错)。当然,你也可以使用 private 修饰槽函数,只是这个 private 修饰符只在直接调用函数时起作用。然而在 Qt 5 中,如果使用新语法就不能这样使用了。

回复
轨迹 2013年5月28日 - 11:12

作为槽,始终是 public 的。哦,原来这样子

回复
Sinn 2013年6月8日 - 23:22

博主,我想问下,main.cpp里你包含的头文件是,而我用也可以通过并成功运行,请问这两者有什么需要注意的地方吗

回复
豆子 2013年6月14日 - 11:08

你的头文件没有写出来哦,用 &lt; 和 &gt; 进行转义

回复
菜青虫1号 2013年7月6日 - 21:26

你好,请问:为什么我复制您的代码却仍有问题:显示连接错误:LNK2001,这是为什么呢?

回复
豆子 2013年7月7日 - 12:48

LNK2001 是链接错误,应该是库没有找到或者其它什么问题。

回复
菜青虫1号 2013年7月7日 - 12:52

你好,谢谢豆子,后来使用了手动执行qmake,就可以了编译通过了,但是在我这边必须手动执行qmake才可以呢?

回复
豆子 2013年7月7日 - 13:37

你的 Q_OBJECT 宏是不是编译过之后才加上上的,或者其它什么原因,导致不是一开始就有这个宏?如果是这样的话,后来加上这个宏的话都必须重新手动 qmake 一下才能检测到新增加的宏。

回复
R 2013年11月28日 - 14:49

豆子,你人太好了,同样的错误,你都会不厌其烦地回答

菜青虫1号 2013年7月7日 - 14:43

啊,原来是这个原因,谢谢豆子~~~ 😀

回复
xiaowoniu 2013年8月28日 - 09:25

写的真好 😛

回复
搁浅的贝 2013年9月27日 - 15:43

QT控制台和纯C/C++的那种控制台有何区别。

QCoreApplication a(argc,argv)做了什么处理?干啥用的。

回复
豆子 2013年9月28日 - 15:19

如果你想知道 QCoreApplication 做了哪些工作,最好去看看 Qt 源代码,这不是很容易就能解释清楚的。不过它与纯 C/C++ 的控制台程序很大的一个区别是在 main() 函数的最后一句:return a.exec();。Qt 会开启一个事件循环,监听程序发出的事件;普通 C/C++ 控制台程序,除非你自己编写事件循环,否则是没有这个实现的。

回复
搁浅的贝 2013年9月27日 - 15:46

问了一出想一出, 🙄 刚接触这个玩意。中文乱码怎么治

之前用QT写纯C的时候乱码。网友说用GBK,还真行。不过在QT控制台里就不行了。

回复
豆子 2013年9月28日 - 15:23

使用 GBK 没有乱码是因为中文 Windows 平台默认编码是 GBK。这个问题没有很通用的解决方案,原因在于 C++ 编译器没有规定文件编码和字符串内部编码,二者一旦不一致就会出现乱码。这个只能由开发人员约定,整个工程使用 UTF-8(一般不是 GBK)文件编码,并且文件中不出现中文。所有中文均通过 tr() 函数提取字符串翻译获得。这样一般不会有编码问题了。

回复
otto 2013年10月11日 - 00:41

楼主你好,我想问个很浅的问题,我断断续续学了很久C++,对于程序内的int main(int argc, char *argv[]),这个的含义一直模糊不清,望楼主告知,知道程序都需要这个,但是不懂

回复
豆子 2013年10月11日 - 09:30

这个问题你应该看看 C++ 的基础。简单来说,main() 函数是所有程序的入口点。一个程序启动所执行的函数就是 main() 函数。它的两个参数是执行程序时所传入的命令行参数。

回复
hysteria 2013年11月29日 - 20:04

/home/hysteria/qt/newspaper/main.cpp:14: 错误:undefined reference to `Newspaper::newPaper(QString const&)'

这是怎么回事阿?代码直接复制你的。。

回复
hysteria 2013年11月29日 - 20:08

QMAKE_CXXFLAGS += -std=c++0x加入这句就没问题了,为什么加入这个就行了呢?

回复
豆子 2013年11月29日 - 22:39

gcc 默认不使用 C++11 标准进行编译,如果要使用 C++11 编译就需要增加编译器参数 -std=c++0x;另外也可以在 pro 文件中增加 CONFIG += c++11,这个需要自己测试下那个能成功

回复
Kab 2013年12月8日 - 19:36

D:\Qtproject\untitled1\main.cpp:46: 错误:newspaper.h: No such file or directory
D:\Qtproject\untitled1\main.cpp:47: 错误:reader.h: No such file or directory
想问一下为什么这样?菜鸟来的,谢谢

回复
豆子 2013年12月9日 - 10:26

看看你的文件是不是在同一目录?一般会是路径的问题。

回复
Kab 2013年12月9日 - 23:59

速度好快啊!原来忘记加入头文件了。谢谢啊

回复
赵楷行 2016年6月28日 - 15:55

Sorry, I'm very very new to Qt——
我在编译过程中出现了"newspaper.h"&"reader.h" no such file or directory 的问题,按照豆子老师在所有相关评论里的解决方案做问题都依然存在。
1、路径里没有中文,且文件都在同一路径下
2、无论Q_OBJECT添加与否,都无法通过通过编译,且重新添加Q_OBJECT后手动执行QMAKE依然没用
3、在.pro文件中添加HEADERS += "newspaper.h"和 HEADERS += " reader.h",或者在mainwindow.h中添加 #include "newspaper.h"和#include "reader.h"也都没有用
4、将main.cpp中的改为"moc_newspaper.h"和"moc_reader.h"也没有用。
5、另,.pro文件中已添加QT += widgets
苯宝宝要炸了,,求豆子教教我!!

回复
赵楷行 2016年6月29日 - 18:07

我知道自己错哪里了。。。

回复
SOLONG 2016年9月5日 - 16:50

最后是怎么解决的啊?我也遇到这样的问题了。谢谢

Highmore 2014年1月3日 - 23:05

爱死这篇文章了。。。帮俺解决了一大串链接问题!!!

回复
河东酷神 2014年1月22日 - 22:49

Qt5不再需要Q_OBJECT宏,已验证

回复
豆子 2014年1月22日 - 23:00

如果你的类里面需要发出信号的话,还是必须使用 Q_OBJECT 宏的,并且 Q_OBJECT 宏不仅仅用于信号槽机制。所以,只要是 QObject 的子类,不管有没有用到信号槽,最好都加上 Q_OBJECT 宏。

回复
河东酷神 2014年1月23日 - 18:23

我有两台电脑装了QT,reader.h将Q_OBJECT 宏注释掉后,在一台编译通过另一台编译未通过。同意你的说法,最好都加上。

回复
豆子 2014年1月23日 - 19:05

可能都是不可用的,之所以有的可以编译通过,可能是因为有之前生成的 moc 之后的文件。你可以完全重新构建一下试试是不是可以。

回复
河东酷神 2014年1月24日 - 10:07

注释掉Q_OBJECT宏,清除并重新构建也可以编译过,并正常运行。就是编译多了一句“reader.h(0): Note: No relevant classes found. No output generated.”

豆子 2014年1月24日 - 15:35

那这个的确比较奇怪的

大灰狼嘎嘎 2014年1月27日 - 11:57

豆哥,Qt4的代码你是这样写的,
QObject::connect(&newspaper, SIGNAL(newPaper(QString)),
&reader, SLOT(receiveNewspaper(QString)));
是不是应该这样?
QObject::connect(&newspaper, SIGNAL(newPaper(const QString)&),
&reader, SLOT(receiveNewspaper(const QString&)));

回复
豆子 2014年1月27日 - 13:40

两种写法都是可以的。Qt 4 的 SLOT 和 SIGNAL 宏在处理之前有一个“标准化”的步骤,会将参数的 const 和引用都去除,所以结果其实是一样的。

回复
Jimmy 2014年2月19日 - 10:06

通常新建项目时,QT都为我们在main.cpp里面自动添加一个QAPPLICATION,你的代码里面使用的是QCoreApplication。我查了下文档,他们的继承关系是QApplication : QCoreApplication : QObject,那么请问有什么特殊的原因要使用QCoreApplication吗?

回复
豆子 2014年2月19日 - 10:13

QApplication 支持 GUI 应用,QCoreApplication 仅支持控制台应用。由于示例代码没有使用 GUI,所以使用了 QCoreApplication。如果在使用 Qt Creator 创建控制台项目时,默认也会给出 QCoreApplication 的。

回复
lee 2014年2月19日 - 14:35

为什么按照楼主的代码打进去,编译出了很多错误,这是什么原因?

D:\Qt\5.2.0\msvc2012\include\QtCore\qpair.h:50: error: C2143: 语法错误 : 缺少“;”(在“'template<'”的前面)
D:\Qt\workspace\5_Slot\main.cpp:20: error: C2628: “Reader”后面接“int”是非法的(是否忘记了“;”?)
D:\Qt\workspace\5_Slot\main.cpp:21: error: C3874: “main”的返回类型应为“int”而非“Reader”
D:\Qt\workspace\5_Slot\main.cpp:30: error: C2664: “Reader::Reader(const Reader &)”: 不能将参数 1 从“int”转换为“const Reader &”
原因如下: 无法从“int”转换为“const Reader”
无构造函数可以接受源类型,或构造函数重载决策不明确
D:\Qt\WorkSpace\5_Slot\reader.h:17: error: C3867: “QMessageLogger::debug”: 函数调用缺少参数列表;请使用“&QMessageLogger::debug”创建指向成员的指针
D:\Qt\WorkSpace\5_Slot\reader.h:17: error: C2297: “<<”: 非法,右操作数包含“const char [18]”类型
D:\Qt\WorkSpace\5_Slot\reader.h:17: error: C2678: 二进制“<<”: 没有找到接受“int”类型的左操作数的运算符(或没有可接受的转换)
D:\Qt\5.2.0\msvc2012\include\QtCore/qchar.h(521): 可能是“QDataStream &operator <<(QDataStream &,QChar)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qbytearray.h(607): 或 “QDataStream &operator <<(QDataStream &,const QByteArray &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qstring.h(1189): 或 “QDataStream &operator <<(QDataStream &,const QString &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qobject.h(549): 或 “QDebug operator <<(QDebug,const QObject *)”
d:\qt\5.2.0\msvc2012\include\qtcore\qcoreapplication.h(270): 或 “QDebug operator <<(QDebug,const MSG &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qiodevice.h(174): 或 “QDebug operator <<(QDebug,QIODevice::OpenMode)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qregexp.h(122): 或 “QDataStream &operator <<(QDataStream &,const QRegExp &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qregexp.h(127): 或 “QDebug operator <<(QDebug,const QRegExp &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qstringlist.h(250): 或 “QDataStream &operator <<(QDataStream &,const QStringList &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qvariant.h(448): 或 “QDebug operator <<(QDebug,const QVariant &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qvariant.h(537): 或 “QDataStream &operator <<(QDataStream &,const QVariant &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qvariant.h(539): 或 “QDataStream &operator <<(QDataStream &,const QVariant::Type)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qvariant.h(835): 或 “QDebug operator <<(QDebug,const QVariant::Type)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qlocale.h(966): 或 “QDataStream &operator <<(QDataStream &,const QLocale &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qlocale.h(971): 或 “QDebug operator <<(QDebug,const QLocale &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qtextstream.h(228): 或 “QTextStream &operator <<(QTextStream &,QTextStreamFunction)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qtextstream.h(231): 或 “QTextStream &operator <<(QTextStream &,QTextStreamManipulator)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qpoint.h(106): 或 “QDataStream &operator <<(QDataStream &,const QPoint &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qpoint.h(206): 或 “QDebug operator <<(QDebug,const QPoint &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qpoint.h(266): 或 “QDataStream &operator <<(QDataStream &,const QPointF &)”
D:\Qt\5.2.0\msvc2012\include\QtCore/qpoint.h(395): 或 “QDebug operator <<(QDebug,const QPointF &)”
尝试匹配参数列表“(int, const QString)”时
D:\Qt\workspace\MakeFile\build-5_Slot-Desktop_Qt_5_2_0_MSVC2012_32bit-Debug\debug\moc_newspaper.cpp:21: error: C2236: 意外的标记“struct”。是否忘记了“;”?
D:\Qt\workspace\MakeFile\build-5_Slot-Desktop_Qt_5_2_0_MSVC2012_32bit-Debug\debug\moc_newspaper.cpp:21: error: C2143: 语法错误 : 缺少“;”(在“{”的前面)
等等

回复
豆子 2014年2月19日 - 15:49

代码可能有错误,比如少了分号之类。这个从错误信息看不出来。

回复
lee 2014年2月19日 - 16:32

之前用的是VS编译的,刚刚重装了一下,用MinGW编译就没有问题了。还是谢谢楼主

回复
豆子 2014年2月19日 - 16:34

这个不大清楚,我也是用 VS 编译的,没有问题。可能哪里有错误

回复
zdt3476 2014年4月21日 - 21:38

qQebug()输出时连双眼号一起输出了,如何解决?

回复
豆子 2014年4月22日 - 09:41

这是 qDebug() 输出的,貌似没有办法解决。不过这一点一般也不需要解决的吧,调试信息格式并不面对用户。

回复
zdt3476 2014年4月22日 - 12:28

恩,这倒也是。不过我是看见你上面说输出格式是:Receives Newspaper: Newspaper A 这样,还以为我哪里弄错了,所以有此一问

回复
Sirit 2014年4月27日 - 07:48

之前看过《C++GUI Qt4编程》,现在豆子把qt5与qt4的代码进行比较,非常好,感谢!

ps:现在网页依然不是满屏呀。

回复
豆子 2014年4月28日 - 10:23

感谢支持!话说主题已经是响应式布局了,完全撑满也不好看的吧~

回复
鹏鹏 2014年5月13日 - 10:52

您好,我想问一下,为什么我的程序第一次运行可以正常运行,再点一次运行就会出现error? 下面是错误信息:
collect2.exe:-1: error: error: ld returned 1 exit status
未找到文件:collect2.exe。

回复
鹏鹏 2014年5月13日 - 11:04

找到原因了,因为之前运行的程序没有关闭,这时如果改变代码再执行程序时就会出现这种错误。
解决方法:关闭QTCreator重新打开就可以了,网上听说还可以通过关闭程序进程的方式,但是我一直没有找到相关的进程。
所以注意及时关闭程序就可以了~

回复
豆子 2014年5月13日 - 14:13

这个只要关闭 Qt Creator 下方那个红色方块按钮就可以了。这是由操作系统运行机制决定的,在 Windows 上就会有这个问题,在 Mac OS X 上就没有。

回复
棚棚 2014年5月13日 - 21:08

嗯是这样的,但是我这里在没有点红色关闭按钮时立即第二次运行就没有那个红色按钮了,这时候又报错,所以只能关闭creator了。

回复
动感超人 2014年5月21日 - 22:54

豆子你好,我想问下信号槽中参数使用引用或指针的时候,connect的连接方式是Qt::QueuedConnection时,Qt是否会把参数拷贝一份放到队列?如果参数是局部变量的话,槽函数中是否会报错?如果参数全局变量的话,能够利用引用或指针的方式在槽函数中达到更改这个全局变量的目的呢?谢谢

回复
豆子 2014年5月23日 - 15:22

Qt::QueuedConnection 主要用于多线程的情况。貌似并没有详细说明会不会将参数加入队列。这种情况下值得注意的是对写参数的加锁这类多线程中需要注意的问题,其它倒是没有注意。至于全局变量的话,就不需要通过参数传递,不是直接可以获得了吗?

回复
动感超人 2014年5月21日 - 23:10

还有我看你的Qt4的示例代码中,信号和槽的声明中参数都是引用类型,但是在connect的时候,两个函数的参数都变成QString类型了,这样也可以吗?

回复
豆子 2014年5月23日 - 14:30

这是可以的,因为 Qt 的 SLOT 和 SIGNAL 宏的连接有一个“格式化”的过程,具体是将 const、引用等修饰全部去除,只有一个最简单的类型作为参数。所以这种连接之后二者其实是等价的。

回复
叔叔级人物 2014年8月22日 - 16:43

Newspaper(const QString & name) :
m_name(name)
{
}
这个构造函数,里面为什么用const QString & name有什么意义?我一直搞不懂!

回复
豆子 2014年8月23日 - 12:04

这是标准 C++ 的内容,说明参数是一个引用,并且函数不改变参数值。这种写法符合软件工程的要求,并且能够提高性能。

回复
叔叔级人物 2014年8月25日 - 10:20

原来这样。感谢!

回复
李小辉爱学习 2014年10月4日 - 16:33

我也是Qt初学者,学生党,我用的是vs2013+Qt5.3.1来编译的,我也遇到了LNK2001的问题,看了很多博客什么的还是不知道怎么办,您说的qmake一下,我还是不知道该怎么操作。

后来问题解决了,情况是这样的,我原来建了一个Qt应用项目名为hello,建好后它本身就有一个hello.h和hello.cpp的文件,我于是又添加了两个头文件reader.h和newspaper.h后来发现编译过程中这两个新的头文件都没有起作用,就算没有include原来的那个hello.h文件,运行的时候程序还总是先mocing hello.h文件,我很不解,然后我把两个头文件里的 东西放到hello.h里之后就能运行了。(感觉都是很深的原理方面的东西,笨笨的我就是搞不明白)还有就是vs2013里面的Qt项目没有pro文件,想知道vcxproj文件是否可以代替,要修改.pro文件的时候总是很烦恼。。。

求指教(>﹏<)

回复
豆子 2014年10月4日 - 16:46

编译器是根据 makefile 进行编译的。手动编写 makefile 非常复杂,因此 Qt 提供了 qmake 工具,根据 pro 文件生成 makefile。有时候增加或删除了头文件,pro 文件没有更新,或者 pro 文件更新了,但是 makefile 没有更新,都会造成类似的结果。因此,重新运行 qmake 的目的是为了生成最新的 makefile

回复
李小辉爱学习 2014年10月4日 - 21:12

WOW,原来是这样,不过我在vs2013里面找不到qmake的选项。。。。(囧)对了,好像豆子哥您也是用vs的吧,我用的是一个add-in的插件把Qt加到vs2013里面的,在这里面对一个Qt项目可以把它转换成QMake Generated project(这样的话就不能使用生成.pro文件的功能),还能转换成Qt Add-in project我不知道这两者的区别并且都没有找到qmake的位置,不过发现了能生成.pro 文件还是很开心的。

求豆子哥指教(作为一个初学者能看到这么一份教程感觉甚是欣慰,十分感谢豆子哥的倾情奉献)

回复
豆子 2014年10月6日 - 17:12

我也一直没有用 VS,都是使用的 Qt Creator 配合 VS 编译器。至于两种项目类型的区别还真不大清楚,看起来 QMake Generated Project 是基于 QMake 的;Qt Add-in Project 则是直接使用了 VS 的项目结构。

回复
寒冰 2016年4月8日 - 11:10

如何根据pro生成makefile或者是dsp,这样可以用VS2010打开了,求指教

回复
豆子 2016年4月11日 - 21:45

当你运行过 qmake 之后,就已经生成 makefile 了。另外,如果安装了 Qt 插件,VS 应该是能够打开的吧(没有测试,不是很了解这部分)。

回复
寒冰 2016年4月11日 - 21:49

VS2010没有安装成功qt5.1.1插件,可以安装qt4.8.5,很纠结,qt4不支持串口通信

肉松 2015年3月5日 - 13:39

我的情况跟你差不多 单独定义reader.h 和 newspaper.h 然后vs报错 找不到moc_read.cpp moc_newspaper.cpp 后来我直接把两个头文件内容放到我一开始新建项目自动生成的头文件中 ok 不知道什么原因....

回复
肉松 2015年3月5日 - 13:43

刚才又试了一下 只要把 那两个头文件先单独编译一下即可....

回复
毕学鸠 2015年1月27日 - 15:39

博主您好。在我的Mac上必须在.pro文件中加上
LIBS += -framework CoreFoundation
才能链接成功。您的PC不需要吗?

回复
豆子 2015年1月28日 - 11:09

CoreFoundation 是 Mac 上面的类库,PC 是不需要的

回复
毕学鸠 2015年1月28日 - 13:43

原来如此,谢谢博主!

回复
于国瑞 2015年1月29日 - 21:40

豆子你好:),我是用VS2013+Qt5.4.0编译的您的代码,但是现在遇到了一个问题。
提示信息如下:
warning MSB8027: Two or more files with the name of moc_reader.cpp will produce outputs to the same location. This can lead to an incorrect build result. The files involved are GeneratedFiles\Debug\moc_reader.cpp, GeneratedFiles\Release\moc_reader.cpp.

Google了一下,有人说是Qt的BUG,但是没有找到解决方案,求解。
谢谢。

回复
于国瑞 2015年1月29日 - 21:44

虽然是Warning,但是导致了下面的情况,所以无法进行了:
fatal error C1083: 无法打开源文件: “GeneratedFiles\Release\moc_reader.cpp”: No such file or directory

回复
于国瑞 2015年1月29日 - 22:47

原因没找到,但是现在解决了问题,重新创建的工程,选择的命令行Qt

回复
小新 2015年5月3日 - 13:29

const QString &name 是什么意思?&name 不是获得指针吗?

回复
小新 2015年5月3日 - 13:49

引用形参,c++正在学习中,之前听说c++有很多的缺陷都没怎么看过,这回要认真看一下

回复
豆子 2015年6月3日 - 09:56

当出现在参数中,& 代表引用传递,而不是按值传递。请参考 C++ 基础有关参数传递的部分。

回复
子愚 2015年6月27日 - 00:56

Ot5.4 creator 分成reader.h newspaper.h
运行时提示
QObject::connect: No such slot Reader::receiveNewspaper(QString) in ..\HelloWorld\main.cpp:47

回复
Napchat 2015年9月16日 - 11:00

我用VS2013+QT5.3.2,在VS2013中创建QT工程,包含两个cpp和一个头文件,我新添加了一个newspaper头文件,最后编译的时候,提示
错误 1 error C1083: 无法打开源文件: “GeneratedFiles\Debug\moc_newspaper.cpp”: No such file or directory c:\Users\Napchat\documents\visual studio 2013\Projects\槽\槽\c1xx 槽

回复
豆子 2015年9月16日 - 12:59

Qt 的项目路径中不允许出现中文,如果确定没有中文,可以尝试 rebuild 一下试试。

回复
Napchat 2015年9月17日 - 09:45

确实如此,中文会显示乱码,已解决,谢豆子
另外问一个问题,就是我现在用QT创建了A、B两个窗体(做界面),准备把B中的信号发到A中的槽里,但我的整个代码是不需要主函数的,我应该把connect函数写在哪里?写在A的cpp里吗?

回复
豆子 2015年9月17日 - 15:13

信号槽主要用于解耦,发出信号的对象不应该知道是谁使用了这个信号;基于这个原则,connect() 函数应该在槽函数的类中。所以按照这个案例,应该是在 A 中更合适一些。

回复
Napchat 2015年9月18日 - 09:00

谢谢豆子!

再见圣堂刺客 2015年11月9日 - 15:05

博主大大,文章写的不错赞一个,就是能不能把博文的字体变成黑体。

回复
豆子 2015年11月9日 - 21:00

黑体主要是怕无法显示加粗,网页上面的显示可能会有不方便的地方;浏览器应该是能够设置默认显示字体的吧?

回复
yuteng 2015年11月15日 - 15:58

工程编译后qdebug无法打印出相应信息?
在工程文件.pro的最后添加:CONFIG += console 才能打印出qdebug信息 ,新手百度出来的。

回复
豆子 2015年11月15日 - 22:42

默认 debug 信息是输出到 Qt Creator 的控制台,而不是系统运行界面的

回复
健胃消食片 2015年12月21日 - 09:15

豆子,这章可以写写信号函数和槽函数参数的一些内容
我开始迷惑于槽函数的参数怎么接收,因为在connect里槽函数参数是函数原型,不知道他如何接收的name参数
第一点:当信号与槽函数的参数数量相同时,它们参数类型要完全一致。
第二点:当信号的参数与槽函数的参数数量不同时,只能是信号的参数数量多于槽函数的参数数量,且前面相同数量的参数类型应一致,信号中多余的参数会被忽略。
此外,在不进行参数传递时,信号槽绑定时也是要求信号的参数数量大于等于槽函数的参数数量。这种情况一般是一个带参数的信号去绑定一个无参数的槽函数。
你能否讲一下参数传递的内部机制
另:感谢教程,非常好

回复
豆子 2015年12月28日 - 17:16 回复
徐立凯 2016年1月5日 - 16:07

为啥你构造的类里面没有析构函数呢??

回复
豆子 2016年1月6日 - 17:00

不显式添加析构函数,编译器会自动添加一个默认析构函数,就像自动添加默认构造函数一样。

回复
一波 2016年4月25日 - 17:35

fatal error C1083: 无法打开源文件: “GeneratedFiles\Release\moc_reader.cpp”: No such file or directory
fatal error C1083: 无法打开源文件: “GeneratedFiles\Release\moc_newspaper.cpp”: No such file or directory

在VS2013中遇到这个问题,上面有俩位同学也遇到了,不过用他们的方法也解决不了。。。挣扎了好一会,,结果,,结果,,只要把Q_OBJECT这个标记删了重新复制一遍就好了。。。。我的真的无语!!为什么会这样

回复
le 2016年5月11日 - 21:16

豆子老师,问你一个关于函数指针的问题,QT里的connect函数中函数指针是函数名前面加上&,但是我查了一下相关资料,对于函数指针,有没有加&是不影响的。可是我把代码中的&去掉之后就会报错,能解释一下吗,我的C++学的不太好请不要见怪。

回复
ben 2016年6月18日 - 20:56

请教豆子:&Newspaper::newPaper --newPaper并不是静态函数啊,直接取地址是怎么取到的?

回复
豆子 2016年6月19日 - 08:33

这是取成员函数地址的语法,并不是静态函数

回复
ZouDikai 2016年8月14日 - 19:26

豆子老师,为什么我把三段代码全放在main.cpp中(已去除重复include的部分),他就会报错:
/home/zdk/singleSlot/main.cpp:46: error: undefined reference to `Newspaper::newPaper(QString const&)'

回复
ZouDikai 2016年8月14日 - 19:28

我没看完就评论了,请无视。。。。。。

回复
Wesly 2016年9月21日 - 17:32

刚买了C++GUI QT4,感觉没有这个教程清晰呢;
现在看你的教程了

回复
yuan 2019年1月23日 - 11:46

这套教程不错

回复
Fantasy 2017年2月10日 - 16:49

评论中如何插入图片呢?

遇到问题,Qt5.5.1 MSVC2013 x86,运行完这段程序退出Qt Creator时,弹窗显示

“程序仍然在运行。
强制关闭吗?”

前面的例程未出现这个状况,是否缺少一个关闭机制之类的?

回复
豆子 2017年2月11日 - 09:06

如果是窗口界面,当窗口关闭时,Qt 会释放资源;如果是控制台,默认不会释放。你看看在 Qt Creator 运行这里会有红色停止按钮,就是有运行的程序没有关闭。

回复
暴雪 2017年3月9日 - 21:34

2012年的好文, 更难能可贵的死2017年豆子仍在回复, 感谢豆子的坚持

回复
Liu 2017年3月18日 - 17:24

豆子哥,我vs2015编译的,编译通过,控制台没输出Receives Newspaper: Newspaper A ,看任务进程,任务正在后台运行。为什么呢?

回复
woshixbt 2019年4月30日 - 09:52

类名是Newspaper,为啥头文件是newspaper

回复
豆子 2019年5月3日 - 10:37

头文件是物理文件名,与类名不一定一致。

回复
FFFFF 2019年7月26日 - 16:28

豆子大大我拷贝了您的代码以后,编译出了问题,提示说 ‘newspaper.h’file not found 和 ‘reader.h’file not found
就不知道怎么解决了

回复
豆子 2019年8月16日 - 21:35

新建这两个文件,然后将对应注释下面的代码复制过去就好了

回复
Nuphy 2020年1月29日 - 11:17

undefined reference to "Newspaper::newPaper(QString const&)"
undefined reference to 'Newspaper::staticMetaObject'
连续六个报错未定义。。在pro文件中加了CONFIG += c++11。。代码是直接复制你的,求教 T T

回复
cj 2020年3月12日 - 20:12

怎么通过的 老哥 我也是这个错误

回复
Nuphy 2020年1月29日 - 12:27

不好意思我没有仔细看,编译通过了····

回复
Amoy 2020年2月13日 - 11:03

很系统,很完整,很有意义的教程,感谢博主!^_^

回复
Abao 2020年7月28日 - 17:16

使用Cmake,报LNK2019和LNK2001错,请问有没有遇到

回复
SHiver 2020年9月21日 - 23:25

博主 复制了一下你的代码 编译遇到个问题 求教
/home/shiverzhao/tools/qt5.12.9/5.12.9/gcc_64/include/QtCore/qobject.h:211: error: expected unqualified-id before ‘=’ token
211 | static QMetaObject::Connection =connect(const QObject *sender, const QMetaMethod &signal,
| ^

回复
SHiver 2020年9月21日 - 23:54

哎 莫名奇妙 用的qt4 重新装了一遍

回复
littlepriest 2023年1月11日 - 16:21

使用的 qt 6.3.1, 如果 Reader.h 中自带默认的构建函数,在 main.cpp 中使用 Reader reader; 会触发报错:
error: call to constructor of 'Reader' is ambiguous

回复

回复 斯啦丝拉 取消回复

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2