Qt信号可以是公有的、保护的或私有的吗?

47

Qt信号可以是公共的、受保护的或私有的吗?我能否创建内部信号,只在类内可见?

更新:我有一个带有一些内部信号的类。如何使这些信号对其他类不可见(封装和信息隐藏)?


在这种情况下,使用PIMPL模式。 - Th. Thielemann
8个回答

29
不能。信号无法被声明为公有或私有。Qt 信号是受保护的类方法。 “signals”关键字在qobjectdefs.h中定义(对于Qt 4.6.1,位于第69行)。
#   define signals protected

更新:信号(signals)在Qt 4的所有小版本中仅为protected。从Qt 5.0开始,它们变为public。请参见https://dev59.com/0mIk5IYBdhLWcg3wl_LE#19130831


5
我认为信号现在被视为“公共的”,请参见此处 https://dev59.com/0mIk5IYBdhLWcg3wl_LE#19130567 - johnbakers
似乎存在私有信号:http://doc.qt.io/qt-5/qstate.html#finished - 那么它们是如何创建的呢? - derM
需要进行更改以在“connect”函数中使用C++11语法,其中提供了对函数的回调,而不是命名信号或插槽。 - mip

19

不错的技巧:如果信号名称为“QPrivateSignal”,则 moc 会忽略信号签名中的最后一项。 - Th. Thielemann
@Th.Thielemann 确实是一个相当糟糕的黑客... - mip

19

通常使用的方法,例如在kdelibs中看到的,是这样的:

Q_SIGNALS:
#ifndef Q_MOC_RUN
    private: // don't tell moc, doxygen or kdevelop, but those signals are in fact private
#endif

   void somePrivateSignal();

这使得信号是私有的,即只能由该类本身发出,而不能由其子类发出。为了不使“private:”覆盖Q_SIGNALS(那么moc将不会将somePrivateSignal视为信号),它被放在Q_MOC_RUN内部,在moc运行时才定义。
编辑:这种方法对于Qt 5中引入的新式连接(connect(a,&A::someSignal,b,&B::someSlot))不起作用,因为它们要求信号可访问。

不,由于“#define Q_SIGNALS protected”,private:将不会有任何效果。 - Frank Osterfeld
1
糟糕,为什么C++中的所有东西都需要一个hack呢? - weberc2
"这种方法不适用于Qt 5引入的新式连接。" -- 取决于... 如果只想允许内部连接到信号(在这种情况下,插槽可能会发出另一个公共信号),那么这是理想的--除了旧语法仍然可以连接到它... - Aconcagua

14

插槽是简单的方法,可以是公共的、受保护的或私有的。

正如Andrei所指出的,信号只是受保护的重新定义,这意味着它们只能由定义它们的类发射。

如果你想让一个类从另一个类发射信号,你需要添加一个公共方法(或插槽),像这样:

void emitTheSignal(...) {
  emit theSignal(...);
}

6
我认为这并没有回答问题的主要疑问。他指的是只能被定义它们的类监听的信号。 - Daniel
它们只能由定义它们的类发出,或者是友元类。 ;) - weberc2
我不确定这是正确的。请看这里:https://dev59.com/0mIk5IYBdhLWcg3wl_LE 文档中说信号可以从其他类中发出,并且始终是公共的,而不是受保护的。 - johnbakers
qobjectdefs.h 中:#define signals protected,所以不,信号并不是 public - gregseth

6
现在您可以使用Q_SIGNAL宏和正常的C++访问说明符来实现。
protected:
  Q_SIGNAL void myProtectedSignal();

private:
  Q_SIGNAL void myPrivateSignal();

4

Qt信号的公共性在于任何对象都可以连接到任何信号。


2
它们也是公共的,因为任何其他类都可以发出它们,因为它们是公共函数。这可能与Qt的先前版本有所不同。请参见此处https://dev59.com/0mIk5IYBdhLWcg3wl_LE#19130567。 - johnbakers
面向对象编程使用封装来避免访问内部方法和数据。如果信号仅用于内部目的,则也应该将其隐藏。 - Th. Thielemann

4

所有现有的答案都是不正确的。

通过在信号的定义中添加QPrivateSignal类型作为最后一个参数,可以将信号设为私有:

signals:
  
  void mySignal(QPrivateSignal);

QPrivateSignal 是由 Q_OBJECT 宏在每个 QObject 子类中创建的私有结构体,因此只能在当前类中创建 QPrivateSignal 对象。

技术上,信号仍具有公共可见性,但它只能由创建它的类发射。


1
你可以使用PIMPL模式来实现。您的私有信号仅存在于私有实现中。

一个从QObject派生的PIMPL会不会使它更重,从而降低性能? - MasterAler
2
是的,这需要一些时间。但请考虑以下问题:调用子程序需要多少时间?相关的程序部分是否与性能有关? - Th. Thielemann
不确定为什么这个被投票否决了。我成功地使用了这种方法,例如用于状态机包装器,它在内部使用QStateMachine但不暴露它(出于架构原因)。同样,我不想公开用于使状态机进行转换的信号,否则这些信号将与公共API信号混合在一起,这将降低可维护性。由于状态机仅在用户交互时进行转换,因此在我的情况下性能影响完全无关紧要。 - Tim Meyer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接