内存中的对象如何组织?
例如,我知道一个函数是内存中的一段代码,它期望通过堆栈和/或寄存器的参数并处理它自己的堆栈帧。
但是对象的结构要复杂得多。他们是如何组织的?每个对象都具有与方法的“链接”,并将地址自身传递给该方法吗?
很高兴看到这个主题的很好的解释。
UPD。我提出的问题更加精确,并且我主要对静态键入语言感兴趣。
内存中的对象如何组织?
例如,我知道一个函数是内存中的一段代码,它期望通过堆栈和/或寄存器的参数并处理它自己的堆栈帧。
但是对象的结构要复杂得多。他们是如何组织的?每个对象都具有与方法的“链接”,并将地址自身传递给该方法吗?
很高兴看到这个主题的很好的解释。
UPD。我提出的问题更加精确,并且我主要对静态键入语言感兴趣。
Answers:
如果没有动态调度(多态性),则“方法”只是含糖函数,可能带有隐式附加参数。因此,struct
出于代码生成的目的,没有多态行为的类的实例实质上是C。
对于静态类型系统中的经典动态调度,基本上有一种主要策略:vtables。每个实例都获得一个附加的指针,该指针引用(类型的有限表示)其类型,最重要的是vtable:一个函数指针数组,每个方法一个。由于在编译时已知每种类型的所有方法的完整集合(在继承链中),因此可以为方法分配连续的索引(对于N个方法为0..N),并通过在函数中查找函数指针来调用方法。使用该索引的vtable(再次将实例引用作为附加参数传递)。
对于更动态的基于类的语言,通常类本身是一类对象,每个对象都具有对其类对象的引用。反过来,类对象以某种语言相关的方式拥有方法(在Ruby中,方法是对象模型的核心部分,在Python中,它们只是带有微小包装的函数对象)。这些类通常还存储对其父类的引用,并将对继承方法的搜索委派给那些类,以帮助元编程增加和更改方法。
还有许多其他的系统不是基于类的,但是它们之间有很大的不同,因此,我只会选择一种有趣的设计替代方法:当您可以在程序中的任何位置随意向所有类型添加新的(一组)方法时(例如Haskell中的类型类和Rust中的traits),则在编译时不知道全部方法。为了解决这个问题,需要为每个特征创建一个vtable ,并在需要特征实现时将它们传递出去。也就是说,这样的代码:
void needs_a_trait(SomeTrait &x) { x.method2(1); }
ConcreteType x = ...;
needs_a_trait(x);
编译为:
functionpointer SomeTrait_ConcreteType_vtable[] = { &method1, &method2, ... };
void needs_a_trait(void *x, functionpointer vtable[]) { vtable[1](x, 1); }
ConcreteType x = ...;
needs_a_trait(x, SomeTrait_ConcreteType_vtable);
这也意味着vtable信息未嵌入对象中。如果要引用“特质实例”,例如当它存储在包含许多不同类型的数据结构中时,它们将正确运行,则可以创建一个胖指针 (instance_pointer, trait_vtable)
。这实际上是上述策略的概括。
这是谚语的答案:“如果给男人一条鱼,你就可以给他喂一天,而如果你教他一个鱼,你就可以终生喂”。
Google的“汇编面向对象程序设计”列出了许多不同的相关资源。
例如,汇编中的面向对象程序设计,Ethan J. Eldridge,2011年12月15日成为第三个链接,看起来不错
通过研究用一些流行的汇编语言编写的源代码并明确声明了OOP,可以看到它的工作原理
通过研究由您选择的OOP编译器生成的中间汇编语言文件,您可以了解它的工作原理。可以配置C ++和FreePascal编译器,以便您可以看到将高级OOP代码转换为汇编语言代码的样子
现在已经太晚了,因为您已经知道其他答案的提示。但是,在互联网如此容易搜索之前,在一些站点可以在几个小时内免费为您免费提供答案的某些志愿者,这些志愿者通过艰辛的方式了解了它,唯一对所有人免费的工作方法是
问问自己:“我将如何实施?”
在经历了几天的背景思考过程并抛弃了一些纸质设计稿后,您常常会发现,您想到的解决方案与其他程序员提出并已经实现的解决方案非常相似
现在是基于我的回答意见,不能直接回答OP的问题,高级用户可以自由投票给我