从Qt的信号槽连接机制调用的槽中抛出异常被认为是未定义的行为,除非它在槽内处理
我希望在其中一个UI对象中设置错误文本。一种优雅的方法是信号/插槽机制。由于我可以在插槽中有异常(如果我也在插槽中处理它们),问题仍然存在:
从异常处理程序中发出信号是否安全?
相关问题是连接机制:
使用排队连接更好(或更糟),还是直接连接好吗?
我想做这样的事情:
class myclass : public QObject
{
Q_OBJECT
public:
myclass() {}
signals:
void error_message(const std::string &);
public slots:
void do_mogrify()
{
try {
// complex function that may throw
} catch (std::exception & e) {
emit error_message(std::string(e.what()));
}
}
}
更新
Craig Scott在他的回答中指出,当执行槽函数时,提供给信号的参数可能不再存在。所以,我已经将示例更改为使用std::string
,如果需要,将复制它(排队连接)。我想它也适用于参考文献。
我想强调一下,关于线程,这个问题不。 Qt Documentation和here以及here涵盖了来自线程的发出信号。
答案 0 :(得分:4)
允许在catch块内发出信号。请仔细考虑您用于信号的参数,记住客户端代码可以选择是直接连接到该信号还是通过排队连接(即您不知道在发出时将使用哪些信号)信号)。在您的示例中,您的信号具有const char*
参数,并且在catch块中,您使用异常' what()
函数作为该参数。如果客户端代码通过排队连接连接到信号,则const char*
参数可能不再存在于调用插槽的位置。如果要从catch块发出信号,请考虑仅使用可安全复制的参数类型(例如,std::string
通过值而不是const char*
传递。)
如果要在非GUI线程中捕获异常并在那里发出信号,则需要排队连接,然后在GUI线程中捕获该信号以向用户显示某种错误消息(仅一个示例)使用场景)。只要您仔细考虑上面提到的参数类型,排队连接就可以了。就个人而言,我认为明智的做法是假设从catch块发出的信号可以通过排队连接进行连接(对未来的更改更加健壮等)。
<强>更新:强>
具体地回答使用排队连接而不是直接连接是好还是坏的问题,这取决于对您来说重要的事情。直接连接是最有效的,并保证您的代码立即响应异常,但您负责线程安全。直接连接实际上只是将信号发射转换为普通的函数调用,尽管在此过程中会有一些内务处理。排队连接会将对异常的响应延迟到将来的任意点,即使它在同一个线程中响应(如果对象拥有,当控制返回到当前线程的事件循环时,将处理排队响应该插槽属于同一个线程,但可能会先处理其他信号。
您的问题没有提到其他连接类型,但默认值为Qt::AutoConnection
,这意味着如果拥有该插槽的对象属于同一个,则行为将与 direct 相同线程或排队如果它属于不同的线程,那么这种连接类型可能会导致您的代码立即响应异常或者可能会延迟,这意味着您需要自己再次处理线程安全。 Qt::BlockingQueuedConnection
可以保证您的代码立即响应,但对性能有潜在的负面影响,需要更加谨慎以避免死锁等。
因此,虽然您更新的问题表明您没有具体询问线程,但线程注意事项与您关于哪种连接类型更好的问题密切相关。最终,它取决于您对更好的标准。
答案 1 :(得分:2)
很安全。从异常处理程序运行代码与从其他地方运行代码没有区别。直接连接和排队连接都同样正常。