有没有办法防止vtable在c ++中发出?

时间:2019-04-13 21:42:36

标签: c++

我正在创建一个库,该库要求类必须继承其他类才能做特定的事情。但是,这不是简单的多态性。这些类是虚拟函数的代码生成器,它们没有数据并且依赖于CRTP,因此它们本身不需要vtable。

是否有一种方法可以阻止为这些类发出vtable?我假设虚拟函数指针将传递给派生类,而虚拟析构函数将跳过这些类。有点像将这些类融为一体。

如果在C ++域中没有通用的通用语言,那么也许只适用于clang,gcc和vc?

示例:

#include<iostream>

template <typename D, typename B>
struct jelly : B
{
  virtual void do_stuff() { static_cast<D*>(this)->D::do_some_other_stuff(); }
};

template <typename D>
struct jelly<D, void>
{
  virtual void do_stuff() { static_cast<D*>(this)->D::do_some_other_stuff(); }
};

struct A : jelly<A, void>
{
  void do_some_other_stuff() { std::cout << "A::do_some_other_stuff()\n"; }
};

struct B : jelly<B, A>
{
  void do_some_other_stuff() { std::cout << "B::do_some_other_stuff()\n"; }
};

int main()
{
  A a;
  a.do_stuff();  // output: A::do_some_other_stuff()
  B b;
  b.do_stuff();  // output: B::do_some_other_stuff()
  A& aa = b;
  aa.do_stuff(); // output: B::do_some_other_stuff()
}

请澄清一下,这只是一个例子。它确实可以运行,但是jelly代表的类数实际上是3个不同的类。一个由开发人员使用jelly库显式继承,另外两个由隐式完成,然后再继承回开发人员自己的类。因为要增加3倍的班级数量,这让我很担心,这就是为什么我问这个问题。

3 个答案:

答案 0 :(得分:1)

我唯一知道的执行此操作的编译器扩展是MSVC的__declspec(novtable)

  

__declspec的这种形式可以应用于任何类声明,但是   应该只应用于纯接口类,即类   永远不会自己实例化。 __declspec停止   编译器从生成代码以初始化vfptr   类的构造函数和析构函数。在很多情况下   删除与vtable相关的对vtable的唯一引用   类,因此链接器将其删除。使用这种形式   __declspec可以大大减少代码大小。

     

如果尝试实例化标记为novtable的类然后访问类成员,则会收到访问冲突(AV)。

当您使用MSVC的__interface关键字时,暗示此修饰符。

答案 1 :(得分:0)

您这样做:

template <typename D, typename B>
struct jelly : B
{
  virtual void do_stuff() { D::do_some_other_stuff(); }
};

然后您这样做:

struct B : jelly<B, A>
{
  void do_some_other_stuff() { std::cout << "B::do_some_other_stuff()\n"; }
};

这意味着struct B取决于jelly<B, A>。那又需要调用B(又名D)函数do_some_other_stuff,但是B尚未定义。也许您需要将void do_stuff移到类声明之外。无论如何,当您声明virtual时,它不应内联。

另外,您正在使用

struct A : jelly<A, void>

翻译为:

struct jelly : void
{
  virtual void do_stuff() { A::do_some_other_stuff(); }
};

您希望如何从void继承?

样式注释:请勿仅使用struct来避免public。 当应该有一个typename D(因为您是继承的)而不是一个class时,请不要使用typename。 不要使用名称A,B,C,D,而是使用一些更具描述性的名称。结构jelly : B是从typename B还是从struct B继承的?

答案 2 :(得分:0)

如果您将成员函数声明为virtual,则该类必须具有实现实现所需的任何机制,以实现C ++要求virtual函数所做的工作。但这表示类型现在是多态的,这要求类型能够执行C ++要求的多态类型可以做到的事情。具体来说,typeiddynamic_cast

那很重要。从多态类型派生的类本身就是多态的,无论它是否覆盖任何virtual函数。这意味着您必须能够从该类的实例获取类型信息。是否实际这样做无关紧要;可以,因此必须存在允许它的机械。

对于vtable实现,这通常意味着每个多态类型都需要一个唯一 vtable对象。除了指向虚拟函数的指针之外,vtable还具有指向某些类型特定信息的索引或指针。由于vtable往往很小,因此围绕另一个vtable并不是特别麻烦。实际上,类型标识信息本身通常比vtable更重要。

现在,编译器提供了一些选项,使您可以删除运行时类型标识的所有痕迹。具体来说,typeid不再起作用,并且dynamic_cast再也不会抛出,因此不需要验证强制类型转换,编译器不再需要为A提供与{{ 1}}。但是,the main target of the feature is the table of type_info objects and other identifying information。因此,我无法谈谈此功能对vtable生成的影响。

但是,最终您无能为力。这些类可能会获取vtable,就是这样。