我想知道,是否可以在接收器位于不同的QThread中时使用Qt信号和槽,同时避免对象切片。
更具体地说,如果使用的 Qt::ConnectionType 是 Qt::QueuedConnection。
首先用一些代码来演示我在说什么。
我正在使用 Qt 6.6.3(MinGW 11.2.0 64 位)。
CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(untitled LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
add_executable(untitled
main.cpp
ParentChildTransfer.h
)
target_link_libraries(untitled Qt${QT_VERSION_MAJOR}::Core)
include(GNUInstallDirs)
install(TARGETS untitled
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
ParentChildTransfer.h
#ifndef PARENTCHILDTRANSFER_H
#define PARENTCHILDTRANSFER_H
#include <QObject>
#include <iostream>
class Parent
{
public:
Parent() = default;
virtual void whoAreYou() const { std::cout << "I am Parent" << std::endl; }
};
class Child : public Parent
{
public:
Child() = default;
void whoAreYou() const override { std::cout << "I am Child" << std::endl; }
};
class Transfer : public QObject
{
Q_OBJECT
public:
Transfer() = default;
public slots:
void valueSlot(const Parent p) { p.whoAreYou(); }
void referenceSlot(const Parent &p) { p.whoAreYou(); }
signals:
void referenceSignal(const Parent &);
};
#endif
main.cpp
#include <QCoreApplication>
#include <QThread>
#include "ParentChildTransfer.h"
int main()
{
char *argv[] = {nullptr};
int argc = 0;
QCoreApplication a(argc, argv);
Transfer thisThreadTransfer;
Transfer *otherThreadTransfer = new Transfer;
QThread otherThread;
otherThreadTransfer->moveToThread(&otherThread);
otherThread.start();
std::cout
<< "Reference signal to value slot in same thread via DirectConnection leads to slicing"
<< std::endl;
QObject::connect(&thisThreadTransfer,
&Transfer::referenceSignal,
&thisThreadTransfer,
&Transfer::valueSlot,
Qt::DirectConnection);
emit thisThreadTransfer.referenceSignal(Child());
thisThreadTransfer.disconnect();
std::cout << "Reference signal to reference slot in same thread via DirectConnection does not "
"lead to slicing"
<< std::endl;
QObject::connect(&thisThreadTransfer,
&Transfer::referenceSignal,
&thisThreadTransfer,
&Transfer::referenceSlot,
Qt::DirectConnection);
emit thisThreadTransfer.referenceSignal(Child());
thisThreadTransfer.disconnect();
std::cout
<< "Reference signal to value slot in other thread via QueuedConnection leads to slicing"
<< std::endl;
QObject::connect(&thisThreadTransfer,
&Transfer::referenceSignal,
otherThreadTransfer,
&Transfer::valueSlot,
Qt::QueuedConnection);
emit thisThreadTransfer.referenceSignal(Child());
QThread::msleep(100);
thisThreadTransfer.disconnect();
std::cout << "Reference signal to reference slot in other thread via QueuedConnection leads to "
"slicing"
<< std::endl;
QObject::connect(&thisThreadTransfer,
&Transfer::referenceSignal,
otherThreadTransfer,
&Transfer::referenceSlot,
Qt::QueuedConnection);
emit thisThreadTransfer.referenceSignal(Child());
QThread::msleep(100);
thisThreadTransfer.disconnect();
std::cout << "Weirdly, reference signal to reference slot in other thread via "
"BlockingQueuedConnection does not lead to slicing"
<< std::endl;
QObject::connect(&thisThreadTransfer,
&Transfer::referenceSignal,
otherThreadTransfer,
&Transfer::referenceSlot,
Qt::BlockingQueuedConnection);
emit thisThreadTransfer.referenceSignal(Child());
thisThreadTransfer.disconnect();
otherThread.quit();
otherThread.wait();
delete otherThreadTransfer;
return 0;
}
控制台输出
Reference signal to value slot in same thread via DirectConnection leads to slicing
I am Parent
Reference signal to reference slot in same thread via DirectConnection does not lead to slicing
I am Child
Reference signal to value slot in other thread via QueuedConnection leads to slicing
I am Parent
Reference signal to reference slot in other thread via QueuedConnection leads to slicing
I am Parent
Weirdly, reference signal to reference slot in other thread via BlockingQueuedConnection does not lead to slicing
I am Child
正如我们所看到的,即使使用引用信号和引用槽,通过 QueuedConnection 我们也可以获得对象切片。
奇怪的是,当使用 BlockingQueuedConnection 时,我们没有得到对象切片。
Qt 文档 指出:
阻塞排队连接 插槽的调用方式与排队连接相同,但当前线程会阻塞,直到插槽返回。
如果这是真的,为什么我们使用 QueuedConnection 获得对象切片,而不是使用 BlockingQueuedConnection 呢?对我来说没有意义...
所以,总结一下:当我想利用 Qt Signal&Slots 在线程之间发送数据时,同时使用 QueuedConnection,有没有办法避免对象切片?
而且,正如刚才提到的,如何解释 QueuedConnection 和 BlockingQueuedConnection 在对象切片方面的差异?
如果您需要避免切片,可能是因为您的设计需要运行时多态性,在这种情况下,Qt 的解决方案与其他任何地方相同:您应该在堆上分配对象并将指针传递给它们,而不是尝试按值传递它们。这样它们就不会被切片。
当然,有一些陷阱需要注意:特别是在堆上分配会导致内存泄漏。出于这个原因,我建议不要使用原始指针,而是使用某种类型的智能指针,这样对象的释放就会自动处理并且不会弄乱。尽管 std::shared_ptr 或 std::unique_ptr 也可以工作,但这里可能使用 Qt QPointer 类。