kod w C++ -> asm

0
#include <iostream>

using namespace std;

class Base {
public:
    
 virtual  void f(int a ){
        cout << "Base::f()\n";
        
    }
};

class Derived : public Base {
public:
    void f( int a ){
        cout << "Derived::f()\n";
        
    }
};


    

int main(int argc, char** argv) {
    Derived obj; 
    Base& ref = obj;
    ref.f(777);
    return 0;
}

http://pastebin.com/gCv6hH1r

Cześć :)
W linku jest kod wygenerowany za pomocą g++ -S. Proszę Was o pomoc we wskazaniu gdzie można zobaczyć w kodzie asm mechanizm późnego wiązania.
Pozdrawiam :)

0
        leaq    -16(%rbp), %rax
        movq    %rax, -8(%rbp)
        movq    -8(%rbp), %rax
        movq    (%rax), %rax
        movq    (%rax), %rdx
        movq    -8(%rbp), %rax
        movl    $777, %esi
        movq    %rax, %rdi
        call    *%rdx
0

no i gdzie Ty tu widzisz niby wirtualność? To, że pojawia się 777? To nic nie znaczy.

0

A rozumiesz jak ona działa? Bo mam wątpliwości. zobacz sobie pod debuggerem jakie pola ma obiekt klasy która ma jakieś metody wirtualne.
Otóż masz tam dodatkowe pole jakim jest tablica wskaźników do metod wirtualnych (virtual methods pointers table). W polimorfiźmie nie ma żadnej magii. Ot zwyczajnie kompilator wie który element tej tablicy przechowuje wskaźnik do danej metody, ale każda klasa ustawia te wskaźniki na swoje implementacje tych metod. W efekcie w kodzie wynikowym widzisz tylko wybranie n-tej metody z tablicy i jej odpalenie, a cały myk polega na tym że dla różnych obiektów w tej tablicy będą różne wskaźniki.

0

bardzo dobrze rozumiem jak działa, dlatego nie widzę tego tu:

       leaq    -16(%rbp), %rax
        movq    %rax, -8(%rbp)
        movq    -8(%rbp), %rax
        movq    (%rax), %rax
        movq    (%rax), %rdx
        movq    -8(%rbp), %rax
        movl    $777, %esi
        movq    %rax, %rdi
        call    *%rdx

A Ty może to widzisz?

Otóż masz tam dodatkowe pole jakim jest tablica wskaźników do metod wirtualnych

No właśnie nie. Polem jest wskaźnik do tablicy.

2

Problem w tym, że kod jest za prosty, a współczesne kompilatory bardzo cwane.

Spróbuj tego:

#include <iostream>
 
using namespace std;
 
class Base {
public:
 
 virtual  void f(int a ){
        cout << "Base::f()\n";
 
    }
};
 
class Derived : public Base {
public:
    void f( int a ){
        cout << "Derived::f()\n";
 
    }
};
 
void callIt(Base *a)  {
     a->f(3);
}

int main(int argc, char** argv) {
    Derived objD; 
    Base objB;
    
    callIt(&objD);
    callIt(&objB);

    return 0;
}
0

Otóż to, już do tego doszedłem, a Ty jesteś pierwszą osobą, która zwraca na to uwagę. Dzięki :)

0

BTW pamięta ktoś stronę która robiła konwersję do kodu maszynowego (nie tylko x86) używając różnych kompilatorów i można było robić porównania?
Pamiętam, że ktoś tu dawał takiego linka, a nie potrafię tego teraz znaleźć.

0

Dobra, zrobiłem Twoją modyfikację, ale dalej nie widzę:
http://pastebin.com/UT7TuEpC

1
mielony napisał(a):

Dobra, zrobiłem Twoją modyfikację, ale dalej nie widzę:
http://pastebin.com/UT7TuEpC

a to co jest:

        .LFB1008:
        .cfi_startproc
        movq    (%rdi), %rax
        movl    $3, %esi
        movq    (%rax), %rax
        jmp     *%rax

Ewidentny skok zależny od zawartości pamięci.

pierwsze movq to odczytanie lokalizacji tablicy wirtualnej, drugie movq to odczytanie lokalizacji metody do wywołania.


Zresztą jak dokładniej czytam posty wyżej to Azarien dobrze ci pokazywał. Tam też masz skok zależny od adresu w pamięci. ```asm leaq -16(%rbp), %rax movq %rax, -8(%rbp) movq -8(%rbp), %rax movq (%rax), %rax ; odczyt lokalizacji tablicy wirtualnej movq (%rax), %rdx ; odczyt adresu metody z tablicy wirtualnej movq -8(%rbp), %rax movl $777, %esi movq %rax, %rdi call *%rdx ; skok zależny od wartości ```

1 użytkowników online, w tym zalogowanych: 0, gości: 1