在为Qt注册自定义类型时何时,何地以及为何使用命名空间

bra*_*aks 11 qt namespaces

类似的问题已多次提出,但我专注于命名空间和指针问题.

MyClass.h

namespace foo {
class MyClass {
   MyClass();
};

QDataStream &operator<<(QDataStream &out, const MyClass & myObj);
QDataStream &operator>>(QDataStream &in, MyClass &myObj);

} // namespace foo

Q_DECLARE_METATYPE(foo::MyClass) // #1
Q_DECLARE_METATYPE(foo::MyClass*) // #2
Run Code Online (Sandbox Code Playgroud)

fooMyClass.cpp(这么多的排列):

MyClass::MyClass()
{
  qRegisterMetaType<MyClass>("MyClass"); // #3
  qRegisterMetaType<MyClass*>("MyClass*"); // #4
  qRegisterMetaType<MyClass>("foo::MyClass"); // #5
  qRegisterMetaType<MyClass*>("foo::MyClass*"); // #6
  qRegisterMetaType<foo::MyClass>("foo::MyClass"); // #7
  qRegisterMetaType<foo::MyClass*>("foo::MyClass*"); // #8
  qRegisterMetaType<MyClass>(); // #9
  qRegisterMetaType<MyClass*>(); // #10
  qRegisterMetaType<foo::MyClass>(); // #11
  qRegisterMetaType<foo::MyClass*>(); // #12

  // same for qRegisterMetaTypeStreamOperators<T>();
}
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,如果我打算在命名空间内部和外部使用信号和槽(可能作为引用和指针)的自定义对象,那么何时以及为什么需要提供命名空间和/或指针变量.我是否总是要完全限定名称空间?

Pav*_*hov 21

我在这个答案中指的是Qt5.Qt4与这个用例不相符.

数据流运营商

如果您只打算在信号和插槽中使用数据流运算符,则不需要数据流运算符.如果要进行某些序列化,则需要它们.

指针,参考和价值观

Qt考虑MyClassMyClass*两种不同的不相关类型.您应该单独申报,注册和使用它们.使用const MyClass &参数类型与MyClassQt元对象系统兼容.请注意,在一个程序中同时使用MyClassMyClass*元类型是不常见的,可能会导致错误和混淆.您应该选择其中一个选项并在整个程序中使用它.还不建议将指针传递给插槽,因为它会导致无法解决的所有权问题.所以我建议使用传递const引用(有时会在Qt信号槽系统内部转换为值传递).如果MyClass对象包含海量数据,则应使用隐式数据共享实现QSharedDataPointer.

声明元类型

首先,您始终需要声明您的元类型:

Q_DECLARE_METATYPE(foo::MyClass)
Run Code Online (Sandbox Code Playgroud)

它在编译时工作,因此对你如何引用你的课程没有限制.以下代码也可以使用:

using namespace foo;
Q_DECLARE_METATYPE(MyClass)
Run Code Online (Sandbox Code Playgroud)

注册元类型

现在你需要注册你的课程.从理论上讲,您需要指定要用于引用类型的所有字符串,即:

qRegisterMetaType<foo::MyClass>("MyClass");
qRegisterMetaType<foo::MyClass>("foo::MyClass");
Run Code Online (Sandbox Code Playgroud)

MyClass在模板参数中引用并不重要.以下代码将类似地工作:

using namespace foo;
qRegisterMetaType<MyClass>("MyClass");
qRegisterMetaType<MyClass>("foo::MyClass");
Run Code Online (Sandbox Code Playgroud)

例如,当您引用信号和插槽时,"MyClass""foo::MyClass"字符串用于标识参数类型SIGNAL(signal1(MyClass)).

新的信号和插槽语法

如果使用带有成员函数指针的新信号槽语法,则只需要使用任意字符串参数进行一次注册.似乎即使没有任何注册也可以使其工作.该文档的这部分指示只会增加Q_DECLARE_METATYPE相反,对这种需要qRegisterMetaType().不幸的是,现在在我的Qt安装中它只适用于直接连接.排队连接仍需要至少一次注册调用.

没有命名空间的类的隐式注册

我在Qt 5.1中尝试了一些注册变体,发现Qt会自动注册没有命名空间的别名.所以,如果你写

qRegisterMetaType<foo::MyClass>("foo::MyClass");
Run Code Online (Sandbox Code Playgroud)

,Qt还会自动注册"MyClass"别名.因此,执行此语句后,您将能够将您的类型称为MyClassfoo::MyClass.文档中没有关于Qt如何处理命名空间的信息.我们可以假设这种行为是有意的,不会在下一版本中删除,但我不会依赖它.以下代码使隐式注册变得明显:

qRegisterMetaType<foo::MyClass>("foo::MyClass");
qRegisterMetaType<bar::MyClass>("MyClass");
Run Code Online (Sandbox Code Playgroud)

Qt 5.1说:

QMetaType :: registerTypedef:二进制兼容性break - 类型名称'MyClass'以前注册为'MyClass'的typedef [1030],现在注册为'bar :: MyClass'的typedef [1032].

Qt 4.8工作没有错误(似乎这个行为尚未在此版本中引入).