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

问题描述 投票:2回答:3

我正在创建一个库,它要求类必须继承其他类来执行特定的操作。但是,这不是简单的多态性。这些类是虚函数的代码生成器,没有数据,依赖于CRTP,因此它们本身不需要vtable。

有没有办法阻止vtable为这些类发出?我假设虚函数指针将传递给派生类,虚拟析构函数将跳过这些类。有点像将类混合成一个。

如果C ++域中没有任何通用可用,那么可能特定于clang,gcc和vc?

Example:

#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库以及其他2个隐式完成的继承显式继承。这是因为课程的数量会增加3倍而令我担心,这也是我提出这个问题的原因。

c++
3个回答
1
投票

我唯一知道的编译器扩展是MSVC的__declspec(novtable)

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

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

使用MSVC的__interface关键字时隐含此修饰符。


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(aka 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,键入一些更具描述性的名称。 struct jelly : B是继承自typename B还是来自struct B


0
投票

如果您将成员函数声明为virtual,那么该类必须具有实现认为必要的任何机制来完成C ++ xzxswpoi函数所需的操作。但这也意味着该类型现在是多态的,这要求该类型能够执行C ++要求多态类型可以执行的操作。具体来说,virtualtypeid

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

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

现在,编译器具有允许您删除运行时类型标识的所有痕迹的选项。具体来说,virtual不再起作用,并且typeid永远不会抛出,因此不需要验证演员阵容,编译器不再需要从dynamic_castA一个不同的vtable。但是,jelly<A>。所以我不能说这个功能对vtable生成的影响。

但最终,你无能为力。那些类可能会得到vtable,就是这样。

© www.soinside.com 2019 - 2024. All rights reserved.