堆栈对象Qt信号和参数作为参考 [英] stack object Qt signal and parameter as reference

查看:157
本文介绍了堆栈对象Qt信号和参数作为参考的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以使用以下代码(最终插槽连接到myQtSignal)吗?

  class Test:public QObject 
{
Q_OBJECT

信号:
void myQtSignal(const FooObject& obj);

public:
void sendSignal(const FooObject& fooStackObject)
{
emit myQtSignal(fooStackObject);
}
};

void f()
{
FooObject fooStackObject;
Test t;
t.sendSignal(fooStackObject);
}

int main()
{
f();
std :: cin.ignore();
return 0;
}

特别是如果emit和slot不在同一个线程中执行。 >

解决方案

UPDATE 20-APR-2015



最初我相信传递一个引用堆栈分配的对象将等同于传递该对象的地址。因此,在没有存储副本(或共享指针)的包装器的情况下,排队的槽连接可能使用坏数据结束。



但是通过@BenjaminT和@cgmb引起我的注意,Qt实际上对const引用参数有特殊的处理。它将调用复制构造函数并收藏复制的对象以用于槽调用。即使你传递的原始对象在插槽运行时被破坏,插槽获取的引用将完全映射到不同的对象。



您可以读取机械细节的@ cgmb的回答。但这里有一个快速测试:

  #include< iostream> 
#include< QCoreApplication>
#include< QDebug>
#include< QTimer>

class Param {
public:
Param(){}
Param(Param const&){
std :: cout< Calling Copy Constructor\\\
;
}
};

class Test:public QObject {
Q_OBJECT

public:
Test(){
for(int index = 0; index< ; 3; index ++)
connect(this,& Test :: transmit,this,& Test :: receive,
Qt :: QueuedConnection);
}

void run(){
Param p;
std :: cout<< 与...发送 & p<< as parameter\\\
;
emit transmit(p);
QTimer :: singleShot(200,qApp,& QCoreApplication :: quit);
}

信号:
void transmit(Param const& p);
public slots:
void receive(Param const& p){
std :: cout< receive called with<< & p<< as parameter\\\
;
}
};

...和主要:

  #include< QCoreApplication> 
#include< QTimer>

#includeparam.h

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

//名称Param必须匹配对工作引用的类型名称(?)
qRegisterMetaType< Param>(Param);

测试t;

QTimer :: singleShot(200,qApp,QCoreApplication :: quit);
return a.exec();
}

运行此操作演示了对于3个插槽连接中的每一个, param通过复制构造函数生成:

 调用复制构造函数
调用复制构造函数
调用复制构造函数
接收以0x1bbf7c0作为参数调用
接收以0x1bbf8a0作为参数调用
接收以0x1bbfa00作为参数调用

你可能想知道,如果Qt只是要做副本,它通过参考做什么好。但是,它并不总是使复制...它取决于连接类型。如果更改为 Qt :: DirectConnection ,则不会生成任何副本:

 以0x7ffebf241147作为参数传送
接收以0x7ffebf241147作为参数调用
接收以0x7ffebf241147作为参数调用
接收以0x7ffebf241147作为参数调用



如果您切换到传递值,您实际上会获得更多中间副本,特别是 Qt :: QueuedConnection case:

 调用复制构造函数
调用复制构造函数
调用复制构造函数
调用复制构造函数
调用复制构造函数
接收使用0x7fff15146ecf作为参数调用
调用复制构造函数
接收调用,使用0x7fff15146ecf作为参数
调用复制构造函数
接收以0x7fff15146ecf作为参数的调用

但是传递指针doesn'做任何特殊的魔法。所以它有在原答案中提到的问题,我会保持下面。但是,结果证明参考处理只是一个不同的野兽。


ORIGINAL ANSWER b
$ b

是的,如果你的程序是多线程的,这可能是危险的。即使不是,它通常风格差。



请注意,Qt支持隐式共享类型,所以传递像QImage的值不会复制,除非有人写他们收到的价值:



http://qt-project.org/doc/qt-5/implicit-sharing.html



问题根本上不是与信号和插槽有关。 C ++有各种各样的方式,当对象在某处被引用时,或者甚至他们的一些代码在调用堆栈中运行时,它们可能被删除。你可以在任何无法控制代码并使用正确同步的代码中轻松地解决这个问题。像使用QSharedPointer的技术可以帮助。



Qt提供了一些额外的有用的东西,以更好地处理删除方案。如果有一个对象要销毁,但你知道它可能正在使用中,你可以使用QObject :: deleteLater()方法:



< a href =http://qt-project.org/doc/qt-5/qobject.html#deleteLater> http://qt-project.org/doc/qt-5/qobject.html#deleteLater a>



这对我来说很方便几次。另一个有用的事情是QObject :: destroyed()信号:



http://qt-project.org/doc/qt-5/qobject.html#destroyed



May I have a "dangling reference" with the following code (in an eventual slot connect to the myQtSignal)?

class Test : public QObject
{
    Q_OBJECT

signals:
    void myQtSignal(const FooObject& obj);

public:
    void sendSignal(const FooObject& fooStackObject)
    {
        emit  myQtSignal(fooStackObject);
    }
};

void f()
{
    FooObject fooStackObject;
    Test t;
    t.sendSignal(fooStackObject);
}

int main()
{
    f();
    std::cin.ignore();
    return 0;
}

Particularly if emit and slot are not executed in the same thread.

解决方案

UPDATE 20-APR-2015

Originally I believed that passing a reference to a stack-allocated object would be equivalent to passing the address of that object. Hence in the absence of a wrapper that would store a copy (or a shared pointer), a queued slot connection could wind up using the bad data.

But it was raised to my attention by @BenjaminT and @cgmb that Qt actually does have special handling for const reference parameters. It will call the copy constructor and stow away the copied object to use for the slot calls. Even if the original object you passed has been destroyed by the time the slot runs, the references that the slots get will be to different objects entirely.

You can read @cgmb's answer for the mechanical details. But here's a quick test:

#include <iostream>
#include <QCoreApplication>
#include <QDebug>
#include <QTimer>

class Param {
public:
    Param () {}
    Param (Param const &) {
        std::cout << "Calling Copy Constructor\n";
    }
};

class Test : public QObject {
    Q_OBJECT

public:
    Test () {
        for (int index = 0; index < 3; index++)
            connect(this, &Test::transmit, this, &Test::receive,
                Qt::QueuedConnection);
    }

    void run() {
        Param p;
        std::cout << "transmitting with " << &p << " as parameter\n";
        emit transmit(p);
        QTimer::singleShot(200, qApp, &QCoreApplication::quit);
    }

signals:
    void transmit(Param const & p);
public slots:
    void receive(Param const & p) {
        std::cout << "receive called with " << &p << " as parameter\n";
    }
};

...and a main:

#include <QCoreApplication>
#include <QTimer>

#include "param.h"

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

    // name "Param" must match type name for references to work (?)
    qRegisterMetaType<Param>("Param"); 

    Test t;

    QTimer::singleShot(200, qApp, QCoreApplication::quit);
    return a.exec();
}

Running this demonstrates that for each of the 3 slot connections, a separate copy of the Param is made via the copy constructor:

Calling Copy Constructor
Calling Copy Constructor
Calling Copy Constructor
receive called with 0x1bbf7c0 as parameter
receive called with 0x1bbf8a0 as parameter
receive called with 0x1bbfa00 as parameter

You might wonder what good it does to "pass by reference" if Qt is just going to make copies anyway. However, it doesn't always make the copy...it depends on the connection type. If you change to Qt::DirectConnection, it doesn't make any copies:

transmitting with 0x7ffebf241147 as parameter
receive called with 0x7ffebf241147 as parameter
receive called with 0x7ffebf241147 as parameter
receive called with 0x7ffebf241147 as parameter

And if you switched to passing by value, you'd actually get a more intermediate copies, especially in the Qt::QueuedConnection case:

Calling Copy Constructor
Calling Copy Constructor
Calling Copy Constructor
Calling Copy Constructor
Calling Copy Constructor
receive called with 0x7fff15146ecf as parameter
Calling Copy Constructor
receive called with 0x7fff15146ecf as parameter
Calling Copy Constructor
receive called with 0x7fff15146ecf as parameter

But passing by pointer doesn't do any special magic. So it has the problems mentioned in the original answer, which I'll keep below. But it has turned out that reference handling is just a different beast.

ORIGINAL ANSWER

Yes, this can be dangerous if your program is multithreaded. And it's generally poor style even if not. Really you should be passing objects by value over signal and slot connections.

Note that Qt has support for "implicitly shared types", so passing things like a QImage "by value" won't make a copy unless someone writes to the value they receive:

http://qt-project.org/doc/qt-5/implicit-sharing.html

The problem isn't fundamentally anything to do with signals and slots. C++ has all kinds of ways that objects might be deleted while they're referenced somewhere, or even if some of their code is running in the call stack. You can get into this trouble easily in any code where you don't have control over the code and use proper synchronization. Techniques like using QSharedPointer can help.

There are a couple of additional helpful things Qt offers to more gracefully handle deletion scenarios. If there's an object you want to destroy but you are aware that it might be in use at the moment, you can use the QObject::deleteLater() method:

http://qt-project.org/doc/qt-5/qobject.html#deleteLater

That's come in handy for me a couple of times. Another useful thing is the QObject::destroyed() signal:

http://qt-project.org/doc/qt-5/qobject.html#destroyed

这篇关于堆栈对象Qt信号和参数作为参考的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆