为什么 C++继承机制不透明?
例如,为什么没有语言支持来检查 vtable?为什么我不能用新成员函数替换成员函数?我有一种直觉,有办法充分利用这些功能。
还有其他语言可以让我做这样的事情吗?
Why, for example, is there no language support to examine a vtable? Why can't I replace a member function with a new one? I have a gut feeling that there are ways to put such features to good use.
Are there any other languages out there which allow me to do such things?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
因为它是编译器的实现细节。该实现可能会发生变化,任何依赖它的代码充其量都是脆弱的。
Because it is an implementation detail of the compiler. That implementation may change, and any code that relies on it would be fragile at best.
C++ 是一种永远不会为不使用的东西“付费”的语言。这种运行时支持将违背这一理念。
有很多语言(在更动态的一端)支持这一点。
C++ is a language where you never 'pay for' what you don't use. This kind of runtime support would run contrary to that philosophy.
There are plenty of languages (on the more dynamic end of the spectrum) that support that.
因为它不必作为
VTable
实现,尽管通常是这种情况。简而言之,C++中不存在VTable
这样的东西!Because it doesn't have to be implemented as a
VTable
, although this is usually the case. In short, there is no such thing asVTable
in C++!主要原因是,将 vtable 作为实现细节允许任何具体实现根据其认为合适的方式对其进行优化;这意味着如果它可以证明不存在对给定方法(或所有方法)的虚拟调用,则它可以修剪甚至完全消除 vtable。或者它可以用 if-else 类型检查替换 vtable 调度,例如它发现只有几个替代方案(这可能是有利的,因为分支预测在这种情况下可以工作,但不适用于 vtable,而且还因为 if-else然后可以内联分支)。它可以对 vtable 中的方法进行重新排序,以便最常调用的方法先出现,或者使通常依次调用的方法填充 vtable 中的相邻槽以利用缓存。等等,等等。当然,所有这些实现也会使 vtable 布局完全不可预测,因此如果将其(通过语言规范)公开给实现,则毫无用处。
同样,虚函数表并不像听起来那么简单。例如,编译器通常必须生成 thunk 来修复诸如虚拟继承或与协变返回类型相结合的多重继承之类的 this 指针。这又是没有“单一最佳方法”来做到这一点的事情(这就是为什么不同的编译器以不同的方式执行它),并且标准化它实际上需要解决特定的方法。
也就是说,如果作为更高级别的构造公开(以便仍然可以进行优化),“vtable 切换”是一种潜在有用的技术。有关示例,请参阅 UnrealScript,它允许为一个类定义多个状态(一个默认,其他命名),并覆盖命名状态中的一些方法。派生类可以重写现有状态中的更多方法,或者添加自己的状态并在其中重写。此外,状态可以扩展其他状态(因此,如果没有为特定状态覆盖某个方法,它将回退到“父”状态,依此类推,直到链达到默认状态)。对于演员建模(游戏本质上就是这样)来说,这一切都很有意义,这就是 UnrealScript 拥有它的原因。所有这一切的明显有效的实现机制是 vtable 切换,每个状态都有一个单独的 vtable。
The main reason is that keeping vtable as an implementation detail allows any concrete implementation to optimize it as it sees fit; this means that it can e.g. trim or even eliminate vtable altogether if it can prove that there are no virtual calls for a given method (or all methods). Or it may replace a vtable dispatch with an if-else type check if e.g. it sees that there are only a few alternatives (this can be advantageous because branch prediction will work in this case, but not with vtables, and also because if-else branches can then be inlined). It can reorder methods in vtable such that most commonly called ones come earlier, or such that those that are commonly called one right after another fill adjacent slots in vtable to take advantage of caching. And so on, and so forth. Of course, all those implementations would also make vtable layout entirely unpredictable, and thus useless, if it were to be exposed (by the language spec) to implementation.
As well, vtables aren't as simple as they sound to be. For example, compilers often have to generate thunks to fix-up
this
pointer for things such virtual inheritance, or multiple inheritance combined with covariant return types. This is again something that doesn't have a "single best way" to do it (which is why different compilers do it differently), and standardizing it would effectively require settling on a particular way.That said, "vtable switching" is a potentially useful technique if exposed as a higher-level construct (so that optimizations are still possible). For an example, see UnrealScript, which allows one to define several states for a class (one default, other named), and override some methods in named states. Derived classes can override more methods in existing states, or add their own states and override in them. Furthermore, states can extend other states (so if a method isn't overridden for a particular state, it falls back to the "parent" state, and so on until the chain reaches the default state). For actor modelling (which games essentially are) this all makes a lot of sense, which is why UnrealScript has it. And the obvious efficient implementation mechanism for all of this is vtable switching, with each state having a separate vtable.
JavaScript、Python 和 Ruby 都可以做到这一点。在这些语言中,类和实例定义在运行时是可变的。抽象地讲,每个对象和类型都是可以检查和更新的成员变量和方法的字典。
这在 C++ 中是不可能的,因为它需要能够重写生成的二进制代码,这可能会带来巨大的性能成本。
JavaScript, Python, and Ruby can all do this. In those languages, class and instance definitions are mutable at runtime. Abstractly, each object and type is a dictionary of member variables and methods that can be examined and updated.
This isn't possible in C++ because it would require being able to rewrite generated binary code, which can carry a substantial performance cost.
虚表仅在某些编译器中的某些情况下存在(即它们未在标准中指定,而是在实现细节中指定)。即使它们确实存在,它们也仅在您具有虚函数并且需要间接实现多态性时才会出现。当不需要时,可以对它们进行优化,从而节省调用时的间接开销。
遗憾的是(或者说,这取决于你对此事的看法;-),C++ 的设计初衷并不是支持猴子补丁。在某些情况下(例如 COM),vtable 是实现的一部分,您也许可以在幕后探索。然而,这永远不会被支持或移植。
Vtables only exist in certain circumstances in some compilers (i.e. they are not specified in the standard but an implementation detail). Even when they do exist, they only occur when you have virtual functions and need the indirection to implement polymorphism. When this is not required they can be optimised out, saving the overhead of the indirection on the call.
Sadly (or otherwise, depending on your views on the matter ;-), C++ was not designed to support monkey patching. In some cases (e.g. COM) the vtable is a part of the implementation and you might be able to poke about behind the scenes. However, this would never be supported or portable.
我相信你可以在 Python 这样的动态语言中做这样的事情:
与 C++ 这样的静态语言的区别在于,解释器在运行时查找所有名称,然后决定要做什么。
在 C++ 中,可能还有其他解决方案来解决“用另一个成员函数替换一个成员函数”的问题,其中最简单的可能是使用函数指针:
(foo 和 bar 不使用 X& 参数,在本例中,类似于 Python例子)
I believe you can do things like that in dynamic languages like Python:
The difference with a static language like C++ is that the interpreter looks up all names at runtime and then decides what to do.
In C++ there are likely other solutions to the "replace a member function with another" problem, the simplest of which might be using function pointers:
(foo and bar don't use the X& argument, in this example, similar to the Python example)
我正在研究一种公开 vtable 的静态编译语言,相信我,它有点
I am working on a statically compiled language that exposes the vtable, and believe me it is quite a bit of hair to expose.
也许您想要的是实现您自己的 vtable,而不使用 C++ 的内置工具。您将会从成员函数指针 (ptmf) 中获得很多乐趣!
您将很难找到具有 vtable 自省功能的编译语言,因为需求很少且不易实现。但对于解释型语言来说,情况恰恰相反。
Maybe what you want is to implement your own vtables without using C++'s built-in facility. You will have a lot of fun with pointer-to-member-functions (ptmf's)!
You will have trouble finding a compiled language with vtable introspection because there's little demand and it's not easy to implement. For interpreted languages, though, the situation is the other way around.
Objective-C(以及 Objective-C++)允许运行时替换已编译的方法。它要么是静态和动态技术的最佳组合,要么是最差的,这取决于你问的是谁。
Objective-C (and Objective-C++ as well) allows the runtime replacement of already compiled methods. It's either the best combination of static and dynamic techniques or the worst, depending on who you ask.
正如其他人指出的那样,C++ 标准中没有“vtable”的概念,因为它只是一种几乎通用的实现技术,很像名称修改。
如果您正在寻找能够以编译语言动态重新定义函数的能力,您可能会对 Common Lisp 感兴趣。肯定还有其他语言,但我能想到的唯一其他语言要么具有静态继承和函数,要么以巨大的性能成本进行解释。
As others have pointed out, there is no concept of "vtable" in the C++ standard, as it's just a nearly universal implementation technique, much like name mangling.
If you're looking for being able to redefine functions on the fly in a compiled language, you might be interested in Common Lisp. There have got to be others, but the only other languages I can think of either have static inheritance and functions or are interpreted at a large cost in performance.