几个月前,当我学会了MIPS汇编时,我脑子里突然出现了一个问题,我忘了问,所以我想我现在就问:
(间接跳转是“跳转寄存器”指令,例如MIPS中的jr $t0
或x86中的jmp EAX
。
我提出的一个这样的解决方案是自修改代码,但我很好奇是否可以通过任何其他方式实现多态(以及OOP)。
答案 0 :(得分:1)
您问题的最简单答案是编写程序(以及可能的汇编程序),以便在运行时解析所有方法调用,从而无需查找表。我假设您正在讨论将子类传递给为超类设计的函数,因此无法实现此优化。
我认为,消除查询超出了你的问题的范围,因此我建议替换jmp <reg>
指令(对不起,我只知道x86)。
call <mem>
地址(这不是你要怎么做的
它使用查找表?)call <reg>
(与jmp完全不同
,但确实回答了你的问题)jmp <mem>
,但是
这与jmp <reg>
所有这些都是可能的,并解决您的问题,但都是相似的。我想这说明了我为什么要做你要问的事情的困惑。您必须有一些方法来选择要调用的方法(vtable)以及将执行转移到方法的某种方式(使用jmp
或call
)。
唯一可行的方法是使用用于指向执行链中下一个命令的寄存器(x86中为EIP
)。你能否或应该是另一个问题。我想如果你对这个架构非常了解并且不担心它会改变你就可以做到。
答案 1 :(得分:0)
您可能需要考虑更改问题
考虑这个C ++代码,Derived
显然是多态的(是的,我没有删除对象):
class Base
{
public: int foo(){ return 1; }
};
class Derived: public Base
{
public: int foo(){ return 2; };
};
int main()
{
Base* b = new Derived();
return b->foo(); //Returns 1
}
gcc生成的程序集是
main:
.LFB2:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $48, %rsp
.seh_stackalloc 48
.seh_endprologue
call __main
movl $1, %ecx
call _Znwm
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rcx
call _ZN4Base3fooEv
addq $48, %rsp
popq %rbp
ret
正如您所看到的,没有间接的跳跃/通话。
那是因为多态不是这里的重点(尽管有必要),虚方法是。
然后答案就是
如果通过间接跳转/调用,则意味着使用运行时值计算跳转/调用目标的每种技术(包括ret
,call []
之类的内容,call reg
,jr
,jalr
)。
考虑这个来源
#include <iostream>
class Base
{
public: virtual int foo(){ return 1; }
};
class Derived: public Base
{
public: int foo(){ return 2; };
};
class Derived2: public Base
{
public: int foo(){ return 3; };
};
int main()
{
int a;
Base* b = 0; //don't remember the header for std::nullptr right now...
std::cin >> a;
if (a > 241)
b = new Derived();
else
b = new Derived2();
return b->foo(); //Returns what?
}
结果取决于用户输入,原型运行时值,因此必须是被调用的例程,没有静态地址。
请注意,在这种情况下,编译器可以使用跳转到具有静态地址的调用(调用Derived2::foo
或Derived::foo
),但这通常不可能(您可能有多个目标文件)没有源,你可能有一个别名指针,指针b
可以由外部库设置,等等。)