我一直在阅读一些关于什么是元类的文章,但我想知道它们是否可以在 C++ 中实现。
我知道 Qt 库正在使用 MetaObjects,但它使用 C++ 的扩展来实现它。我想知道是否可以直接在 C++ 中使用。
谢谢。
C++ 没有对元类的内置支持(不是以 Python/Objective-C 方式),但是您可以手动模仿元类的行为。基础非常简单,您创建一个生命周期更长的额外类(单例、静态对象或Construct On First Use Idiom),它能够创建和操作它的相应类。(在 Objective-C 中,元类通常包含“静态”成员变量、内存分配/释放例程等)。
Qt 所做的是,他们采用了元类的概念并对其进行了修改,以便它们可以支持某种形式的反射(以及不支持它的系统上的 RTTI)。实现这一点将需要大量的宏魔术或自定义编译器(例如他们选择使用的)。
不过一般来说,常规元类提供的大部分特性已经由 C++ 语言提供。只是以不同的形式。实际上,您需要元对象的唯一原因是出于反射目的,如本文档中所述,在 C++ 中实现反射有不同的方法。
除此之外,如果您真的设置在 Objective-C 风格的元类系统上,我不知道有任何库可以做到这一点,但很可能有。另一方面,自己滚动也不应该那么困难。
Gamma 等人的“设计模式”一书中的一些模式与元类概念具有相似的特征。例如,“策略”模式允许您在创建时自定义对象行为。信封成语是另一个紧密的匹配。但是,也不允许您自定义类的接口。
在 COM 中,IDispatch 接口允许在运行时向对象动态添加方法/属性。不过,这意味着放弃标准的 c++ 方法调用。每个调用都通过相同的方法,该方法将索引或字符串键放入用户维护的方法/属性表中,并且所有参数必须作为对象数组传递。
使用这两种技术,您可以获得元类的灵活性,但由于没有语法优势或运行时/编译器帮助,这是一个更难的过程。
戴夫
如果元类的工作定义是“实例化本身就是类的语言实体”,那么泛型就是 C++ 中的元类:
#include <iostream>
using namespace std;
template <typename T>
class Meta {
public:
Meta(const T&init) : mData(init) {}
// ...
private:
T mData;
};
int main(int, char **) {
cout << "The size of Meta<double> is " << sizeof(Meta<double>) << endl ;
return 0;
}
在倒数第二行中使用 Meta<double> 会强制编译器实例化 Meta<double> 类;sizeof 运算符对 Meta 进行操作,从而证明这不仅仅是语义糖,而且该类已被实例化。即使没有实例化 Meta 类型的对象,该程序也是完整的。
不怕……至少不是本地人。
拥有一个元类通常需要一个代表类的运行时对象,就像 Java 中的情况一样。
在 C++ 中,类没有运行时表示。它们的表现形式出现在虚拟桌子之类的东西中。但在许多方面,它们像 C 函数一样运行,所有 OOP 几乎就像胶水代码一样运行。
话虽如此,您可能希望使用其他语言的元类来实现许多事情的 OOP 模式。您还可以“模拟”您自己的对象系统。
可以创建元类,但是 C++ 不是这样,它是关于基于静态编译时的实现,而不是运行时的灵活性。
无论如何,这取决于您是否想要带有方法的元类或仅带有数据的元类,可以使用诸如 boost::any 之类的 Boost 构造来实现数据类,如果您想要带有方法的类,则可以使用 boost::bind 来将方法绑定到对象,或者您可以使用单入口点接口(如 COM 对象)自行实现它们。
然而,“真正的”C++ 方法是使用泛型,因此它可以在编译时确定,以获得最佳性能。
老实说,我见过很少的系统,尽管我见过一些真正需要运行时灵活性的系统,在大多数情况下,对象在同一个类中诞生和消亡,或者至少足以将其 95% 的生命周期作为一个单一的他们从工厂出来后上课。
因此,在许多情况下,人们会发现自己为运行时元类付出了太多。当然,有一种观点认为这可以提供更好的开发人员性能,但在许多情况下,每行代码在硬件上运行的时间是编写它所花费的时间的几亿倍。因此,您可以将编译时和运行时类视为预付费用或永久租用。我个人喜欢先付钱。
您可能想看看这篇博文。
https://herbsutter.com/2017/07/26/metaclasses-thoughts-on-generation-c/
元类可能会进入 C++,但还需要一段时间。