没有间接跳跃的多态性?
当我几个月前学习MIPS大会时,一个问题突然出现在我的脑海中,我忘了问什么,所以我想现在就问它:
是否有可能在没有间接跳转指令的情况下实现多态? 如果是这样,怎么样?
(间接跳转是“跳转寄存器”指令,例如MIPS中的jr $t0
或x86中的jmp EAX
。)
我提出的一个这样的解决方案是自修改代码,但我很好奇多态性(以及OOP)是否可以通过任何其他方式。
对你的问题最简单的回答是编写你的程序(和可能的汇编程序),这样所有的方法调用都可以在运行时被解析,从而不需要查找表。 我假定您正在讨论将子类传递给为超类设计的函数,因此不可能实现此优化。
我认为,消除查找不在你的问题的范围之内,所以我将建议替换jmp <reg>
指令(对不起,我只知道x86)。
call <mem>
(这不是你怎么用查找表执行的?) call <reg>
(与jmp不完全不同,但是可以回答你的问题) jmp <mem>
,但这与jmp <reg>
并不jmp <reg>
所有这些都是可能的,并解决你的问题,但都是一样的。 我想这说明了我为什么想要做你想问的事情的困惑。 你必须有一些方法来选择调用哪个方法(vtable)以及某种方式将执行转移到方法(使用jmp
或call
)。
唯一的另一种可能的方式是使用用于指向执行链中下一个命令的寄存器(x86中的EIP
)。 无论你可以或应该是另一个问题。 我想如果你对这个架构非常了解,并且不担心它会改变,你可以做到这一点。
是
不过,您可能需要考虑更改您的问题
考虑这个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
可以由外部库设置,等等)。
上一篇: Polymorphism Without Indirect Jumps?
下一篇: How do I xcodebuild a static library with Bitcode enabled?