没有间接跳跃的多态性?

时间:2011-06-14 07:56:39

标签: oop assembly polymorphism

几个月前,当我学会了MIPS汇编时,我脑子里突然出现了一个问题,我忘了问,所以我想我现在就问:

是否可以在没有间接跳转指令的情况下实现多态?如果是这样,怎么样?

(间接跳转是“跳转寄存器”指令,例如MIPS中的jr $t0或x86中的jmp EAX

我提出的一个这样的解决方案是自修改代码,但我很好奇是否可以通过任何其他方式实现多态(以及OOP)。

2 个答案:

答案 0 :(得分:1)

您问题的最简单答案是编写程序(以及可能的汇编程序),以便在运行时解析所有方法调用,从而无需查找表。我假设您正在讨论将子类传递给为超类设计的函数,因此无法实现此优化。

我认为,消除查询超出了你的问题的范围,因此我建议替换jmp <reg>指令(对不起,我只知道x86)。

  • 您可以在内存上执行call <mem> 地址(这不是你要怎么做的 它使用查找表?)
  • 您可以在注册表上执行call <reg> (与jmp完全不同 ,但确实回答了你的问题)
  • 如果您愿意,可以jmp <mem>,但是 这与jmp <reg>
  • 没有什么不同

所有这些都是可能的,并解决您的问题,但都是相似的。我想这说明了我为什么要做你要问的事情的困惑。您必须有一些方法来选择要调用的方法(vtable)以及将执行转移到方法的某种方式(使用jmpcall)。

唯一可行的方法是使用用于指向执行链中下一个命令的寄存器(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

正如您所看到的,没有间接的跳跃/通话。

那是因为多态不是这里的重点(尽管有必要),虚方法是。

然后答案就是

如果通过间接跳转/调用,则意味着使用运行时值计算跳转/调用目标的每种技术(包括retcall []之类的内容,call regjrjalr)。

考虑这个来源

#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::fooDerived::foo),但这通常不可能(您可能有多个目标文件)没有源,你可能有一个别名指针,指针b可以由外部库设置,等等。)