为 Qt 注册自定义类型时,何时、何地以及为何使用命名空间
Posted
技术标签:
【中文标题】为 Qt 注册自定义类型时,何时、何地以及为何使用命名空间【英文标题】:When, where and why use namespace when registering custom types for Qt 【发布时间】:2014-02-26 14:43:15 【问题描述】:类似的问题已经多次提出,但我关注的是命名空间和指针问题。
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
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>();
所以我的问题是,如果我打算将自定义对象用于命名空间内部和外部的信号和插槽(可能作为引用和指针),何时以及为什么需要提供命名空间和/或指针变体.我是否总是必须完全限定命名空间?
【问题讨论】:
【参考方案1】:我在这个答案中指的是 Qt5。 Qt4 不适合这个用例。
数据流运算符
如果您只打算在信号和槽中使用它,则您的类型不需要数据流运算符。如果您想进行一些序列化,它们是必需的。
指针、引用和值
Qt 考虑了MyClass
和MyClass*
两种不同的不相关类型。您应该分别声明、注册和使用它们。使用const MyClass &
参数类型与Qt 元对象系统中的MyClass
兼容。请注意,在一个程序中同时使用 MyClass
和 MyClass*
元类型是不寻常的,可能会导致错误和混乱。您应该选择其中一个选项并在整个程序中使用它。也不建议将指针传递给插槽,因为它会导致无法解决的所有权问题。所以我建议使用通过 const 引用传递(有时会在 Qt 信号槽系统内部转换为按值传递)。如果MyClass
对象包含海量数据,则应使用QSharedDataPointer
实现隐式数据共享。
声明元类型
首先,你总是需要声明你的元类型:
Q_DECLARE_METATYPE(foo::MyClass)
它在编译时工作,因此对引用类的方式没有限制。以下代码也可以使用:
using namespace foo;
Q_DECLARE_METATYPE(MyClass)
注册元类型
现在您需要注册您的课程。从理论上讲,您需要指定要用于引用您的类型的所有字符串,即:
qRegisterMetaType<foo::MyClass>("MyClass");
qRegisterMetaType<foo::MyClass>("foo::MyClass");
在模板参数中如何引用MyClass
并不重要。以下代码将类似地工作:
using namespace foo;
qRegisterMetaType<MyClass>("MyClass");
qRegisterMetaType<MyClass>("foo::MyClass");
例如,"MyClass"
和 "foo::MyClass"
字符串用于在引用 SIGNAL(signal1(MyClass))
之类的信号和槽时标识参数类型。
新的信号槽语法
如果您使用带有指向成员函数的指针的新信号槽语法,您只需使用任意字符串参数进行一次注册。即使没有任何注册,它似乎也打算让它工作。 This part of the docs 指示仅添加 Q_DECLARE_METATYPE
,与需要 qRegisterMetaType()
的 this 相反。不幸的是,现在在我的 Qt 安装中,它只适用于直接连接。排队的连接仍然需要至少一个注册调用。
没有命名空间的类的隐式注册
我在 Qt 5.1 中尝试了一些注册变体,发现 Qt 会自动注册没有命名空间的别名。所以如果你写
qRegisterMetaType<foo::MyClass>("foo::MyClass");
,Qt 会另外自动注册"MyClass"
别名。因此,执行此语句后,您将能够将您的类型称为MyClass
和foo::MyClass
。文档中没有关于 Qt 如何处理命名空间的信息。我们可以假设这种行为是有意的,并且不会在下一个版本中删除,但我不会依赖它。下面的代码使隐式注册显而易见:
qRegisterMetaType<foo::MyClass>("foo::MyClass");
qRegisterMetaType<bar::MyClass>("MyClass");
Qt 5.1 说:
QMetaType::registerTypedef: 二进制兼容性中断 -- 类型名称 'MyClass' 以前注册为 'MyClass' [1030] 的 typedef,现在注册为 'bar::MyClass' [1032] 的 typedef。
Qt 4.8 可以正常工作(这个版本似乎还没有引入这种行为)。
【讨论】:
你的回答真的是权威!你能不能也添加一些关于嵌套类的cmets?我经常使用struct XyzService struct Result ... ; Result doWork(...); ;
的样式当我使用qRegisterMetaType<XyzService::Result>("XyzService::Result")
注册XyzService::Result
时,我看到有关信号槽连接的未注册类型的警告。但是,"Result"
工作正常。当然,这会导致多个嵌套的Result
类型(具有不同的外部类)的命名冲突。 :(
以上是关于为 Qt 注册自定义类型时,何时、何地以及为何使用命名空间的主要内容,如果未能解决你的问题,请参考以下文章
何时何地根据实际宽度更新 UITableViewCell 子视图的约束
Visual Studio 2017 何时、何地以及如何设置 DOCKER_BUILD_SOURCE 环境变量