我尝试使用谷歌搜索,但没有找到任何结果。使用函数指针和数据的结构,实现类似于C中的类hack吗?或者它是以另一种形式实现的?
答案 0 :(得分:2)
我没有编写任何c ++编译器,但类的结构应该包含:
注意:非虚拟函数是静态引用的,因此它们不应直接绑定到类实例。
也就是说,每个编译器可能会以不同的方式实现它。
TLDR :基本上是的,实现必须类似于类hack(但这应该对开发人员来说是透明/无关的。)
修改强>:
这篇文章主要是基于多年来使用visual studio进行调试(非常主观)的推测,多年前支持一些经验,维护一个具有类hack支持继承的项目,用C实现(这种体验非常主观,如同孔)。
例如,在Visual Studio 6中,您可以看到虚拟函数表是在c ++实现特定数据之前分配的。也就是说,一个班级看起来像这样:
[vtbl][data]
^1 ^2
所以,如果这是(例如)struct X { virtual ~X(); int i; }
,那么写:
X a;
X *p = &a;
会创建类似于此的内容:
[ptr + 1] -> any other virtual functions
[ptr + 0] -> X::~X
^x
[^x][data]
^1 ^2
^p = ^2;
^1
是操作系统分配内存的位置(并且vtbl将在new
的实现中填充),那么用户数据的偏移量(vtbl + sizeof (vtbl)
)将是返回到客户端代码,作为类的地址。我不知道是否仍然如此。
答案 1 :(得分:2)
是的,通常(或者至少有时候)它将以一种可以使其与“C-hack”兼容的方式实现,但细节因编译器而异。
由于以下几个原因,这变得非常自然:
基本上,以一种可以在随附的C实现中描述类布局的方式来实现C ++是“容易的”,但也不“不”这样做。“/ p>
作为GCC布局的一个简单例子,我们可以考虑使用虚方法的类(如果它没有虚方法,那么它将是一个简单的struct layoutwise)。然后,布局将以虚拟表指针开始,该指针指向一个单词/指针数组(包含函数指针,指向type_info
节点的指针,以及一些其他有用信息)。然后跟随成员,好像它是一个普通的结构。
当继承它类似时,它从基类的布局开始(自然因为指向派生类的指针应该很容易转换为基类的指针),除非现在虚拟表指针指向虚拟表派生类的顺序(顺便说一下,它具有与基类相同的布局,除了它可能有与派生类中引入的新虚拟方法相对应的附加元素 - 这里指向派生类的虚拟表的指针可以作为基类的虚拟表)。
在C中,这看起来像是:
struct class_layout {
void **__vptr;
/*
base data members as they appear in the C++ definitions,
(given they have fundamental types)
*/
/*
additional data members introduced in the derived class
*/
};
将__vptr
定义为包含函数指针的struct
的指针是很诱人的,它会正常工作,除非它与GCC的工作方式不兼容。细节是虚拟表中的条目也是负指数(可能由于历史原因)。
然后有些情况需要特别小心:例如,没有虚方法的基类(几乎)要求基类中没有虚拟表指针。虚拟继承的情况需要指向基类对象的指针。
答案 2 :(得分:1)
通常,答案是"是",因为这是最直接的方式。但是,绝不是保证。标准没有说明编译器必须做什么,它只是说明在给定条件下必须发生的事情。
在最简单,最简单的情况下,class
与成员默认为struct
的{{1}}相同。由于你无法对private
做任何事情(一切都是私密的,没有公共成员,没有构造函数),所以它毫无意义。
在下一个最简单的情况下,struct
也将具有成员函数,包括构造函数和析构函数。
成员函数隐含地具有不同的调用约定,因为传递了隐式class
指针,但除此之外它们只是普通的普通函数。通常情况下,编译器会将名称变成类似this
的内容,其中每个" magic"字母和数字具有明确的,依赖于编译器的含义(我试图在GCC中一起修改一个正确的错误名称,手动执行这一操作有点单调乏味)。
在创建和销毁对象时,编译器还将代表自己调用构造函数和析构函数。除此之外,这些基本上都是普通的普通函数"(我们将在这里忽略很少的额外怪癖)。
然后,可能有虚函数。为了实现这一点,通常,在结构的数据部分前面添加一个指向全局函数指针表的指针(每个对象一个指针,但对于该类型的所有对象,全局只有一个表)。
再次,就像破坏一样,这是一个实现细节,而不是标准要求的东西(实际上,它是如何在几乎所有地方完成的)。
在最复杂的情况下,添加了几个虚函数指针,并且编译器根据当前对象的类型秘密调整对象的指针(投射指针可能确实给出了不同的地址) ,也)。这可以确保"正确"在不必知道的情况下调用虚函数。
答案 3 :(得分:1)
您可能会发现此演示文稿既有趣又有用: http://www.hexblog.com/wp-content/uploads/2011/08/Recon-2011-Skochinsky.pdf
通常,非虚拟类的布局或多或少与C中的布局类似。
虚拟类必须存储虚函数表,指向运行时类型信息的指针和'偏移',以便可以在任意复杂的派生类中找到数据。
这三个对象被折叠到虚拟功能表中。