编译后的C ++类是什么样的? [英] What does a compiled C++ class look like?

查看:82
本文介绍了编译后的C ++类是什么样的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有了一些汇编指令和C程序的背景知识,我可以形象地看到一个已编译的函数的样子,但是有趣的是,我从未如此仔细地考虑过一个已编译的C ++类的样子.

With some background in assemble instructions and C programs, I can visualize how a compiled function would look like, but it's funny I have never so carefully thought about how a compiled C++ class would look like.

bash$ cat class.cpp
#include<iostream>
class Base
{
  int i;
  float f;
};

bash$ g++ -c class.cpp

我跑了

bash$objdump -d class.o
bash$readelf -a class.o

但是我很难理解.

有人可以向我解释一下还是提出一些好的出发点.

Could somebody please explain me or suggest some good starting points.

推荐答案

这些类(或多或少)被构造为常规结构.这些方法(或多或少...)被转换为第一个参数为"this"的函数.对类变量的引用是对"this"的偏移.

The classes are (more or less) constructed as regular structs. The methods are (more or less...) converted into functions which first parameter is "this". References to the class variables are done as an offset to "this".

就继承而言,让我们引用C ++ FAQ LITE中的引用(在此处进行镜像)

As far as inheritance, lets quote from the C++ FAQ LITE, which is mirrored here http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.4 . This chapter shows how Virtual functions are called in the real hardware (what does the compile make in machine code.

让我们举个例子.假设Base类具有5个虚函数:virt0()virt4().

Let's work an example. Suppose class Base has 5 virtual functions: virt0() through virt4().

 // Your original C++ source code
 class Base {
 public:
   virtual arbitrary_return_type virt0(...arbitrary params...);
   virtual arbitrary_return_type virt1(...arbitrary params...);
   virtual arbitrary_return_type virt2(...arbitrary params...);
   virtual arbitrary_return_type virt3(...arbitrary params...);
   virtual arbitrary_return_type virt4(...arbitrary params...);
   ...
 };

步骤#1 :编译器会构建一个包含5个功能指针的静态表,将该表埋入某个位置的静态内存中.许多(并非全部)编译器在编译.cpp时定义了该表,该.cpp定义了Base的第一个非嵌入式虚拟函数.我们称该表为v表.我们假设其技术名称为Base::__vtable.如果功能指针适合目标硬件平台上的一个机器字,则Base::__vtable最终将占用5个隐藏的内存字.每个实例不5个,每个函数不5个;仅5.它可能类似于以下伪代码:

Step #1: the compiler builds a static table containing 5 function-pointers, burying that table into static memory somewhere. Many (not all) compilers define this table while compiling the .cpp that defines Base's first non-inline virtual function. We call that table the v-table; let's pretend its technical name is Base::__vtable. If a function pointer fits into one machine word on the target hardware platform, Base::__vtable will end up consuming 5 hidden words of memory. Not 5 per instance, not 5 per function; just 5. It might look something like the following pseudo-code:

 // Pseudo-code (not C++, not C) for a static table defined within file Base.cpp

 // Pretend FunctionPtr is a generic pointer to a generic member function
 // (Remember: this is pseudo-code, not C++ code)
 FunctionPtr Base::__vtable[5] = {
   &Base::virt0, &Base::virt1, &Base::virt2, &Base::virt3, &Base::virt4
 };

第2步:编译器向Base类的每个对象添加了一个隐藏的指针(通常也是一个机器字).这称为v指针.将这个隐藏的指针视为一个隐藏的数据成员,就好像编译器将您的类重写为如下所示:

Step #2: the compiler adds a hidden pointer (typically also a machine-word) to each object of class Base. This is called the v-pointer. Think of this hidden pointer as a hidden data member, as if the compiler rewrites your class to something like this:

 // Your original C++ source code
 class Base {
 public:
   ...
   FunctionPtr* __vptr;  ← supplied by the compiler, hidden from the programmer
   ...
 };

第3步:编译器在每个构造函数中初始化this->__vptr.这个想法是使每个对象的v指针指向其类的v表,就像在每个构造函数的init-list中添加以下指令一样:

Step #3: the compiler initializes this->__vptr within each constructor. The idea is to cause each object's v-pointer to point at its class's v-table, as if it adds the following instruction in each constructor's init-list:

 Base::Base(...arbitrary params...)
   : __vptr(&Base::__vtable[0])  ← supplied by the compiler, hidden from the programmer
   ...
 {
   ...
 }

现在让我们计算一个派生类.假设您的C ++代码定义了从类Base继承的类Der.编译器重复步骤1和3(但不重复2).在步骤#1中,编译器创建一个隐藏的v表,保留与Base::__vtable中相同的功能指针,但替换那些与替代相对应的插槽.例如,如果Der覆盖virt0()virt2()并按原样继承其他对象,则Der的v表可能看起来像这样(假设Der没有添加任何新的虚拟对象):

Now let's work out a derived class. Suppose your C++ code defines class Der that inherits from class Base. The compiler repeats steps #1 and #3 (but not #2). In step #1, the compiler creates a hidden v-table, keeping the same function-pointers as in Base::__vtable but replacing those slots that correspond to overrides. For instance, if Der overrides virt0() through virt2() and inherits the others as-is, Der's v-table might look something like this (pretend Der doesn't add any new virtuals):

 // Pseudo-code (not C++, not C) for a static table defined within file Der.cpp

 // Pretend FunctionPtr is a generic pointer to a generic member function
 // (Remember: this is pseudo-code, not C++ code)
 FunctionPtr Der::__vtable[5] = {
   &Der::virt0, &Der::virt1, &Der::virt2, &Base::virt3, &Base::virt4
 };                                        ^^^^----------^^^^---inherited as-is

在步骤3中,编译器在每个Der构造函数的开头添加了一个类似的指针分配.想法是更改每个Der对象的v指针,使其指向其类的v表. (这不是第二个v指针;它是在基类Base中定义的同一v指针;请记住,编译器不会在类Der中重复步骤2.)

In step #3, the compiler adds a similar pointer-assignment at the beginning of each of Der's constructors. The idea is to change each Der object's v-pointer so it points at its class's v-table. (This is not a second v-pointer; it's the same v-pointer that was defined in the base class, Base; remember, the compiler does not repeat step #2 in class Der.)

最后,让我们看看编译器如何实现对虚函数的调用.您的代码可能如下所示:

Finally, let's see how the compiler implements a call to a virtual function. Your code might look like this:

 // Your original C++ code
 void mycode(Base* p)
 {
   p->virt3();
 }

编译器不知道这是要调用Base::virt3()还是Der::virt3(),还是要调用另一个尚不存在的派生类的virt3()方法.它只能确定您正在调用virt3(),它恰好是v表的插槽#3中的函数.它将该调用重写为如下形式:

The compiler has no idea whether this is going to call Base::virt3() or Der::virt3() or perhaps the virt3() method of another derived class that doesn't even exist yet. It only knows for sure that you are calling virt3() which happens to be the function in slot #3 of the v-table. It rewrites that call into something like this:

 // Pseudo-code that the compiler generates from your C++

 void mycode(Base* p)
 {
   p->__vptr[3](p);
 } 


我强烈建议每个C ++开发人员阅读FAQ.这可能要花几周的时间(因为它很难阅读且很长),但是它将教给您很多有关C ++的知识以及如何使用C ++.


I strongly recommend every C++ developer to read the FAQ. It might take several weeks (as it's hard to read and long) but it will teach you a lot about C++ and what can be done with it.

这篇关于编译后的C ++类是什么样的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆