公共职能与公共职位

时间:2016-03-08 22:43:35

标签: qt signals-slots

在我Qt编程的一年中,我学到了很多关于信号和插槽的知识。但还不够......

http://doc.qt.io/qt-5/signalsandslots.html

  

插槽可用于接收信号,但它们也是正常的   成员职能。

那么......有没有理由不将一个继承自QObject的类中的每个函数声明为一个插槽,无论它是否需要为一个?

在上面的链接中,他们举了一个例子:

  

一个基于QObject的小类可能是:

#include <QObject>

class Counter : public QObject
{
    Q_OBJECT

public:
    Counter() { m_value = 0; }

    int value() const { return m_value; }

public slots:
    void setValue(int value);

signals:
    void valueChanged(int newValue);

private:
    int m_value;
};

为什么将value()函数定义为普通函数而不是插槽?如果它们确实成为一个插槽,会有任何负面结果吗?

5 个答案:

答案 0 :(得分:13)

回到过去,如果你想连接信号,你别无选择,只能使用插槽。在Qt 5中不再是这种情况,可以在常规成员函数甚至自由函数或lambda中建立连接。

声明一个槽将该函数注册到该特定对象的元数据中,使其可以随时用于依赖于元对象的所有Qt功能。除此之外,它是文档所述的常规成员函数。插槽函数本身没有什么特别之处,不同之处在于它在元对象中生成元数据。

这意味着在编译时间和可执行文件大小方面,声明插槽都需要付出一些代价,虽然很小。我说将所有公共功能都作为插槽是太过分了。在实际需要插槽时仅使用插槽会更有效,如果它不能与常规功能一起使用,则将其作为插槽。

另外,请注意,在几乎所有情况下,都会使用返回类型void声明信号。这与信号的典型使用情况有关 - 它们通常可以传递参数,但很少返回任何信息。即使可以通过连接到插槽的信号返回值,也很少使用它。因此,声明一个函数将某些东西作为您想要连接的插槽返回是没有多大意义的,因为它返回一个值意味着它很可能不会在典型的信号/插槽上下文中使用。这就是为什么在这个例子中getter不是一个槽。作为插槽的setter在Qt 5中是多余的,并且可能是这个示例代码可以追溯到Qt 4的产物。

最后,将插槽与常规公共函数分开是一种很好的方式来说明意图,或者如果你愿意的话,那就是该类的“API”。例如,我在扩展QML时主要使用插槽,因此我不必将每个函数显式标记为可调用 - 与上一段中提到的场景不同,此类插槽通常返回内容,但它们并未真正用于连接。通过这种方式,我可以清楚地了解该类提供的接口设计。

答案 1 :(得分:2)

除了ddriver的答案,这是最好的/正确的答案(+1那里),我还认为将所有成员函数定义为公共插槽是令人困惑的。他们定义函数的方式(私有/公共/插槽等...)会对类的感知用法产生影响。

我的意思是......你可以说所有功能都应该是公共的(或公共插槽),然后这涵盖了所有情况。然而,这可能会让您的班级的未来用户感到困惑。考虑int value()是一个公共插槽(它不是最好的例子),有人可以尝试将它作为一个,但函数本身有一个返回值,对于一个插槽真的没有意义。它对正常的成员函数有意义,其中(作为普通函数调用)可以访问返回值。

要遵循的一条规则是始终将您的函数和变量保持为本地范围和私有(默认情况下),并且仅将其打开以供其他用途(公共,插槽,全局等...)当你真的需要它们的时候。这样可以使您的类界面更容易理解,并避免后续用户混淆。

我确信这个经验法则有一个名称,所以你可以在一些编码技术网站上查找,但我不记得了:(

修改

另一个小例子是自动完成...如果您的所有功能都是插槽,那么当您执行connect(this, myClass::mySignal, &someOtherClass, SomeOtherClass::<auto complete options> );时,您的自动完成选项列表可能很长而且不清楚是什么。如果你只有一些特定的成员是插槽,那么更容易知道选择哪个。

答案 2 :(得分:2)

关于来自code_fodder的回答的封装,应该注意的是,真的没有私有插槽。

例如:

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass()
        :QObject(NULL) {}

private slots:
    void Hello() { qDebug("Hello World\n"); }
};

#include "main.moc"

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

    MyClass* cls = new MyClass;
    QMetaObject::invokeMethod(cls, "Hello");

    return a.exec();
}

正如我们在这里看到的那样,在课堂外调用函数Hello是成功的。

答案 3 :(得分:0)

您可能不希望所有方法都是插槽的另一个用例是当您通过Qt's WebKit Bridge向JavaScript公开对象时: 所有公共插槽都可以从JavaScript调用,这可能会导致安全问题,具体取决于JavaScript是否可信任。

因此,如果您想要一个不能从JavaScript调用的公共方法,则不能将其声明为插槽。

答案 4 :(得分:-1)

对于某些功能,您需要一个返回值。这在插槽中不会那么容易。在插槽中,您无法使用函数的返回值或为它们提供引用参数。是的,你可以做到,但你有时间问题。 您是否使用插槽或普通成员功能依赖于您的软件架构。

此外,插槽在事件循环中运行。这取决于你的代码是否有意或无意。