我正在创建一个库,它要求类必须继承其他类来执行特定的操作。但是,这不是简单的多态性。这些类是虚函数的代码生成器,没有数据,依赖于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
库以及其他2个隐式完成的继承显式继承。这是因为课程的数量会增加3倍而令我担心,这也是我提出这个问题的原因。
我唯一知道的编译器扩展是MSVC的__declspec(novtable)
:
这种形式的
__declspec
可以应用于任何类声明,但应该只应用于纯接口类,即永远不会自己实例化的类。__declspec
阻止编译器生成代码以初始化类的构造函数和析构函数中的vfptr。在许多情况下,这将删除与该类关联的vtable的唯一引用,因此链接器将删除它。使用这种形式的__declspec
可以显着减少代码大小。如果您尝试实例化标记有
novtable
的类,然后访问类成员,您将收到访问冲突(AV)。
使用MSVC的__interface
关键字时隐含此修饰符。
你做:
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
?
如果您将成员函数声明为virtual
,那么该类必须具有实现认为必要的任何机制来完成C ++ xzxswpoi函数所需的操作。但这也意味着该类型现在是多态的,这要求该类型能够执行C ++要求多态类型可以执行的操作。具体来说,virtual
和typeid
。
这很重要。派生自多态类型的类本身就是多态的,无论它是否覆盖任何dynamic_cast
函数。这意味着您必须能够从该类的实例获取类型信息。你是否真的这样做是无关紧要的;你可以,因此机器必须存在才能允许它。
对于vtable实现,这通常意味着每个多态类型都需要具有唯一的vtable对象。除了指向虚函数的指针之外,vtable还有一个指向某些类型特定信息的索引或指针。因为vtable往往很小,所以另外一个vtable并不是特别麻烦。实际上,类型识别信息本身通常比vtable更重要。
现在,编译器具有允许您删除运行时类型标识的所有痕迹的选项。具体来说,virtual
不再起作用,并且typeid
永远不会抛出,因此不需要验证演员阵容,编译器不再需要从dynamic_cast
给A
一个不同的vtable。但是,jelly<A>
。所以我不能说这个功能对vtable生成的影响。
但最终,你无能为力。那些类可能会得到vtable,就是这样。