【问题标题】:Disable dynamic binding (virtual table creation) in c++ for virtual functions在 C++ 中为虚拟函数禁用动态绑定(虚拟表创建)
【发布时间】:2015-09-10 09:57:41
【问题描述】:

我最近遇到了一个 C++ 面试问题,这让我很感兴趣:

假设您错误地将某些 C++ 成员函数声明为虚拟函数,但(可能出于性能原因)您希望阻止编译器为该函数创建 v-table。也就是说,禁用动态函数绑定以支持静态绑定。

您将如何实现这一目标?另外,是否有一些 C++11 特定的方法?

【问题讨论】:

  • 重写代码库中依赖于动态绑定的所有内容...:D(说真的,在面试中我总是对愚蠢的问题给出愚蠢的答案,它有效)
  • 如果你使用虚函数,你就有了一个虚表。如果您不想要虚拟表,请不要使用虚拟功能
  • @Gombat 如果你有一个 vtable 仅仅是编译器实现特定的,c++ 标准没有说明这样的概念。
  • @stgatilov:但是如果您只是删除 virtual 关键字,您的程序将无法正常运行。 :D
  • 也许这个问题真的很糟糕,或者你改变了解释问题的意思。不管怎样,也许他们只是在问如何绕过虚拟绑定并直接调用特定类的绑定,如name::function(...)

标签: c++ performance c++11 vtable


【解决方案1】:

我知道没有办法强制 C++ 编译器禁用动态绑定,除非它支持这样的选项(并非所有 C++ 编译器都这样做,但大多数编译器都这样做)。但是,这有点像把婴儿和洗澡水一起扔了,因为它在理论上禁用了所有不属于 C 的 C++ 功能。

当然,在 C++11 中引入了 final 标识符,它可以防止从类中进一步派生或覆盖虚拟成员。严格来说,这并不能阻止动态调度 - 它解决了一个不同的问题。

避免动态绑定的影响(感知或实际)的一种方法是避免使用或编写任何具有虚成员函数的类,并且不创建类层次结构(即不要从具有虚函数的类派生)。显然,如果没有虚函数在起作用,就不需要虚函数调度,因此不需要动态绑定。

如果您知道对象的类型,则可以通过使用静态调度来避免使用动态绑定,即显式命名要调用的函数。例如,假设我们有一个类Base,它提供一个名为foo()publicvirtual 成员和一个名为Derived 的类,它继承自Base 并覆盖foo()。那么下面就避免做动态调度了;

 Base *b = new Derived;
 b->Base::foo();          //   static call;  will not call `Derived::foo()`
 b->Derived::Foo();       // incorrect static call.  Will not compile since b is a pointer to Base not Derived

 Derived *d = new Derived;
 d->Derived::foo();       // static call of Derived::foo()
 d->Base::foo();          // static call of Base::foo()

当然,如果使用对象的代码依赖于对象的 ACTUAL 类型的知识,或者依赖于被调用的 foo() 的特定变体,那么它的设计有点违背了拥有多态基础的目的类和其他派生自它的类。

在上面,编译器仍将支持虚函数调用(vtable 等,如果现在编译器可以工作的话),这可能会影响创建和销毁对象的过程。

另一种避免动态分派(或绑定)的技术是使用模板(有时称为编译时多态性)。本质上,模板可能假设一个类型提供了一些接口(或一组操作),并且可以使用该接口的任何类型的变量。例如;

 struct X
 {
      void foo();
 };

 template<class T> void func()
 {
      T x;          // relies on T being instantiable (and destructible)
      x.foo();      // relies on T having a member named foo()
 }

 // in some function somewhere where both X and func() are known to the compiler

 func<X>();

此类模板不需要T 类型具有虚函数,因此不要依赖动态调度(绑定)。但是,没有什么可以阻止这样的模板函数与具有虚拟成员函数的类一起使用,因此这不会禁用动态绑定 - 它只允许程序员做出选择以避免使用动态绑定。

如果我在面试中被问到这个问题,我可能会指出以上所有内容,但不要说这个问题相当愚蠢。熟悉 C++ 的面试官会意识到这一点,并且只会对你如何思考和解决这样的问题感兴趣(毕竟现实世界的开发人员经常被管理层或客户要求满足愚蠢或不切实际的要求,并且期望是机智的足以避免告诉他们的经理或客户他们很愚蠢)。如果面试官在不理解的情况下提出了这个问题(或者在房间里没有其他理解这一点的面试小组成员),我无论如何都不想与那个雇主合作。

【讨论】:

  • 说得好。谢谢彼得。
【解决方案2】:

您可以通过禁用 RTTI 来避免开销...有一个编译时开关。

一旦启用 RTTI 禁用标志,对于 dynamic_cast/typeid 将不会有任何虚拟表调度开销。

【讨论】:

  • 但是在禁用 rtti 之前,您必须注意 dynamic_cast/type id 的使用。我认为这就是面试官的意思。。
  • 没有 RTTI 并不意味着没有 vtables。
  • 同意。但是问题中的“禁用动态绑定功能”让我想,也许这就是面试官的意思。
  • 感谢所有 cmets。如果关闭 RTTI 的任何编译时间标志,请告诉我。这可能是我问题的可能答案。
猜你喜欢
  • 2017-07-02
  • 2020-07-02
  • 2016-11-26
  • 1970-01-01
  • 1970-01-01
  • 2012-04-18
  • 2018-11-29
  • 1970-01-01
  • 2011-01-11
相关资源
最近更新 更多