Полиморфизм без косвенных скачков? - PullRequest
2 голосов
/ 14 июня 2011

Когда я изучал сборку MIPS несколько месяцев назад, в моей голове возник вопрос, который я забыл задать тогда, поэтому я решил задать его сейчас:

Можно ли реализовать полиморфизм без инструкций непрямого перехода? Если да, то как?

(Косвенные переходы - это инструкции «регистра перехода», например, jr $t0 в MIPS или jmp EAX в x86.)

Одним из таких решений, которое я придумал, является самоизменяющийся код, но мне любопытно, возможен ли полиморфизм (и, следовательно, ООП) любым другим способом.

Ответы [ 2 ]

1 голос
/ 14 июня 2011

Простейшим ответом на ваш вопрос было бы написание вашей программы (и, возможно, ассемблера) таким образом, чтобы все вызовы методов могли быть разрешены во время выполнения, что устраняет необходимость в таблице поиска.Я предполагаю, что вы говорите о передаче подкласса функции, которая была разработана для суперкласса, и поэтому невозможно реализовать эту оптимизацию.

Исключение поиска, я думаю, выходит за рамкивашего вопроса, поэтому я собираюсь предложить замены для jmp <reg> инструкции (извините, я знаю только x86).

  • Вы можете выполнить call <mem> для адреса памяти (невот как бы вы это делали, используя справочную таблицу?)
  • Вы можете выполнить call <reg> в регистре (не совсем отличается от jmp, но отвечает на ваш вопрос)
  • Вы можетеjmp <mem> если хотите, но это не все, что отличается от jmp <reg>

Все это возможно и решит вашу проблему, но все они одинаковы.Я думаю, это иллюстрирует мое замешательство, почему вы хотели бы сделать то, что вы просите.У вас должен быть какой-то способ выбрать, какой метод вызывать (vtable), и какой-то способ передать выполнение методу (используя jmp или call).

Единственный другой возможный способ сделать этобудет играть с регистром, который используется для указания на следующую команду в цепочке выполнения (EIP в x86).Можете ли вы или должны это другой вопрос.Я полагаю, если бы вы были хорошо осведомлены об архитектуре и не беспокоились о ее изменении, вы могли бы сделать это.

0 голосов
/ 25 июня 2015

Да

Возможно, вы захотите изменить вопрос, однако

Рассмотрите этот код 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 может быть установлен внешней библиотекой и т. д.).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...