0

我有相当多的类都派生自同一个父级,并且需要从一个简单的 QString 实例化其中的任何一个

我想象使用带有 QString 键的 QMap 和对类的“引用”作为值可以解决问题

class Feat : public QObject { ... } // Base class

QMap<QString, ...> feats;

但我不知道从这里去哪里,得到我想要的东西的语法是什么。

4

2 回答 2

1

鉴于您的类继承自QObject,您可以使用QMetaType系统从它们的名称作为字符串构造实例。元对象系统是 Qt 的关键部分,提供类型名称和类型ID之间的映射。

您需要首先注册您的类型,以便元对象系统知道如何处理它,使用:

Q_DECLARE_METATYPE(Feat)

然后,每当您想按其名称构造对象时,您将使用:

int type_id = QMetaType::type("Feat");
void *obj = QMetaType::create(type_id);

现在obj指向动态分配的Feat. 请注意,完成后需要释放对象(使用QMetaType::destroy(type_id, obj););将其包装在某种受保护的指针中(std::unique_ptr,QPointer等);或确保它有一个QObject会自动释放它的父级。

另外,请注意,您需要以这种方式注册您想使用的每种类型。

于 2019-01-01T21:52:03.630 回答
0

由于构造函数不是“普通”函数,因此您需要类似工厂模式的东西,即存储返回给定类型的新实例的函数;一种简单的 C++11+ 方法是将 a 映射QStringstd::function返回的新实例(作为指向基类的指针):

QMap<QString, std::function<Feat *()>> feats;
feats["Feat1"] = []() { return new Feat1; };
feats["Feat2"] = []() { return new Feat2; };

如果您需要传入参数以进行构造,或者您希望返回一些智能指针(例如 a unique_ptr),请根据需要进行调整。此外,根据您的口味,您可能更喜欢或不喜欢将此代码隐藏在宏后面。

#define REGISTER_FEAT(FeatClass) feats[#FeatClass] = []() { return new FeatClass;}
// to be used as
REGISTER_FEAT(Feat1);
REGISTER_FEAT(Feat2);

std::function如果您确定不需要在那些 lambda 中捕获任何内容,您也可以避免开销,但除非您有一些真正的理由(构建时间?运行时开销?)来避免它,否则我不会担心这一点。


顺便说一句,即使是 Digia 也承认 Qt 容器处于“生命支持模式”,因此,除非您有充分的理由使用它们(例如,稳定的二进制接口,跨不同编译器的性能更一致 - 尽管通常更差),否则您通常会得到更好的服务由内置的“STL”对应物(例如std::mapstd::unordered_map)。

于 2019-01-01T21:11:27.960 回答