Public 函数与 public 槽
Public functions versus public slots
在我一年的Qt编程中,我学到了很多关于信号和槽的知识。但还不够...
http://doc.qt.io/qt-5/signalsandslots.html
Slots can be used for receiving signals, but they are also normal
member functions.
那么...是否有任何理由不将从 QObject
继承的 class 中的每个函数都声明为插槽,无论它是否需要一个?
在上面的link中,他们举了一个例子:
A small QObject-based class might read:
#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()
函数定义为普通函数而不是插槽?如果他们真的让它成为一个插槽,会有任何负面结果吗?
对于某些函数,您需要 return 值。这在插槽中不会那么容易工作。在插槽中,您不能使用函数的 return 值或为它们提供参考参数。是的,你可以做到,但你有时间问题。
您是使用插槽还是普通成员函数取决于您的软件架构。
此外,事件循环中的插槽 运行。这是有意还是无意,取决于您的代码。
在过去,如果你想连接到信号,你别无选择,只能使用插槽。在 Qt 5 中不再是这种情况,在 Qt 5 中可以连接到常规成员函数甚至自由函数或 lambda。
声明一个插槽寄存器,该寄存器在该特定对象的元数据中发挥作用,使其可用于所有依赖于该元对象的 Qt 功能。除此之外,正如文档所述,它是一个常规成员函数。槽函数本身并没有什么特别之处,区别在于元对象中为其生成元数据。
这意味着声明插槽需要付出一些代价,尽管在编译时间和可执行文件大小方面都是很小的代价。我会说将所有 public 函数都作为插槽是多余的。仅当您确实需要插槽时才使用插槽会更有效,如果它不能与常规功能一起使用,请将其设为插槽。
此外,请注意,在几乎所有情况下,信号都是使用 return 类型 void
声明的。这与信号的典型使用案例有关——它们通常可以传递参数,但很少 return 传递任何东西。尽管可以通过连接到插槽的信号 return 值,但这种情况极少使用。因此,将 return 的函数声明为您打算连接的插槽没有多大意义,因为它 return 是一个值这一事实意味着它很可能不会在典型的 signal/slot 上下文中使用。这就是为什么 getter 在该示例中不是插槽的原因。 setter 作为插槽在 Qt 5 中是多余的,并且可能是可追溯到 Qt 4 的示例代码的产物。
最后,将插槽与常规 public 函数分开是说明意图的好方法,或者 class 的 "API" 如果您愿意的话。例如,我在扩展 QML 时主要使用插槽,因此我不必将每个函数显式标记为可调用 - 与上一段中提到的场景不同,此类插槽通常 return 东西,但它们实际上并不是在连接中使用。通过这种方式,我可以清楚地了解 class 提供的界面设计。
除了 ddriver 的答案,这是最佳/正确答案(+1),我还认为将所有成员函数定义为 public 槽是令人困惑的。他们定义函数的方式(private / public / slots 等...)对 class.
的感知使用有影响
我的意思是....您可能会争辩说所有函数都应该只是 public(或 public 插槽),然后这涵盖了所有情况。但是,这可能会让 class 的未来用户感到困惑。考虑到 int value()
是一个 public 插槽(这不是最好的例子)有人可以尝试将它用作一个插槽,但函数本身有一个 return 值,这对一个插槽。对于普通成员函数来说确实有意义,其中(作为普通函数调用)可以访问 return 值。
要遵守的一条规则是,始终将您的函数和变量保持在局部范围内并尽可能私有(默认情况下),并且仅将它们打开以供其他用途(public-ness、插槽、全局等...) 当你真正需要它们时。这使您的 class 界面更易于理解,并避免以后的用户混淆。
我确定这个经验法则有一个名称,因此您可以在一些编码技术网站上查找它,但我想不起来了:(
编辑
另一个小例子是自动完成...如果您的所有函数都是插槽,那么当您执行 connect(this, myClass::mySignal, &someOtherClass, SomeOtherClass::<auto complete options> );
时,您的自动完成选项列表可能很长而且不清楚什么是什么。如果您只有几个特定的插槽成员,那么更容易知道选择哪个。
参考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();
}
正如我们在这里看到的,从 class.
外部调用函数 Hello
是成功的
另一个您可能不希望所有方法都成为插槽的用例是当您通过 Qt's WebKit Bridge 将对象公开给 JavaScript 时:
所有 public 插槽都可以从 JavaScript 调用,这可能会引发安全问题,具体取决于 JavaScript 是否可信。
因此,如果您想要一个无法从 JavaScript 调用的 public 方法,您可能不会将其声明为插槽。
在我一年的Qt编程中,我学到了很多关于信号和槽的知识。但还不够...
http://doc.qt.io/qt-5/signalsandslots.html
Slots can be used for receiving signals, but they are also normal member functions.
那么...是否有任何理由不将从 QObject
继承的 class 中的每个函数都声明为插槽,无论它是否需要一个?
在上面的link中,他们举了一个例子:
A small QObject-based class might read:
#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()
函数定义为普通函数而不是插槽?如果他们真的让它成为一个插槽,会有任何负面结果吗?
对于某些函数,您需要 return 值。这在插槽中不会那么容易工作。在插槽中,您不能使用函数的 return 值或为它们提供参考参数。是的,你可以做到,但你有时间问题。 您是使用插槽还是普通成员函数取决于您的软件架构。
此外,事件循环中的插槽 运行。这是有意还是无意,取决于您的代码。
在过去,如果你想连接到信号,你别无选择,只能使用插槽。在 Qt 5 中不再是这种情况,在 Qt 5 中可以连接到常规成员函数甚至自由函数或 lambda。
声明一个插槽寄存器,该寄存器在该特定对象的元数据中发挥作用,使其可用于所有依赖于该元对象的 Qt 功能。除此之外,正如文档所述,它是一个常规成员函数。槽函数本身并没有什么特别之处,区别在于元对象中为其生成元数据。
这意味着声明插槽需要付出一些代价,尽管在编译时间和可执行文件大小方面都是很小的代价。我会说将所有 public 函数都作为插槽是多余的。仅当您确实需要插槽时才使用插槽会更有效,如果它不能与常规功能一起使用,请将其设为插槽。
此外,请注意,在几乎所有情况下,信号都是使用 return 类型 void
声明的。这与信号的典型使用案例有关——它们通常可以传递参数,但很少 return 传递任何东西。尽管可以通过连接到插槽的信号 return 值,但这种情况极少使用。因此,将 return 的函数声明为您打算连接的插槽没有多大意义,因为它 return 是一个值这一事实意味着它很可能不会在典型的 signal/slot 上下文中使用。这就是为什么 getter 在该示例中不是插槽的原因。 setter 作为插槽在 Qt 5 中是多余的,并且可能是可追溯到 Qt 4 的示例代码的产物。
最后,将插槽与常规 public 函数分开是说明意图的好方法,或者 class 的 "API" 如果您愿意的话。例如,我在扩展 QML 时主要使用插槽,因此我不必将每个函数显式标记为可调用 - 与上一段中提到的场景不同,此类插槽通常 return 东西,但它们实际上并不是在连接中使用。通过这种方式,我可以清楚地了解 class 提供的界面设计。
除了 ddriver 的答案,这是最佳/正确答案(+1),我还认为将所有成员函数定义为 public 槽是令人困惑的。他们定义函数的方式(private / public / slots 等...)对 class.
的感知使用有影响我的意思是....您可能会争辩说所有函数都应该只是 public(或 public 插槽),然后这涵盖了所有情况。但是,这可能会让 class 的未来用户感到困惑。考虑到 int value()
是一个 public 插槽(这不是最好的例子)有人可以尝试将它用作一个插槽,但函数本身有一个 return 值,这对一个插槽。对于普通成员函数来说确实有意义,其中(作为普通函数调用)可以访问 return 值。
要遵守的一条规则是,始终将您的函数和变量保持在局部范围内并尽可能私有(默认情况下),并且仅将它们打开以供其他用途(public-ness、插槽、全局等...) 当你真正需要它们时。这使您的 class 界面更易于理解,并避免以后的用户混淆。
我确定这个经验法则有一个名称,因此您可以在一些编码技术网站上查找它,但我想不起来了:(
编辑
另一个小例子是自动完成...如果您的所有函数都是插槽,那么当您执行 connect(this, myClass::mySignal, &someOtherClass, SomeOtherClass::<auto complete options> );
时,您的自动完成选项列表可能很长而且不清楚什么是什么。如果您只有几个特定的插槽成员,那么更容易知道选择哪个。
参考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();
}
正如我们在这里看到的,从 class.
外部调用函数Hello
是成功的
另一个您可能不希望所有方法都成为插槽的用例是当您通过 Qt's WebKit Bridge 将对象公开给 JavaScript 时: 所有 public 插槽都可以从 JavaScript 调用,这可能会引发安全问题,具体取决于 JavaScript 是否可信。
因此,如果您想要一个无法从 JavaScript 调用的 public 方法,您可能不会将其声明为插槽。