2015-12-20 13 views
5

Devatisimin nasıl işleyeceğini görmek için bu kısa programı yazdım. derleyici doğru tip anlamak mümkün olmalıdır: Derleyici devirtualization, çok akıllı değil mi?

#include <iostream> 
using std::cout; 
using std::endl; 

class Base 
{ 
public: 
    void foo() { cout << "Base::foo" << endl; } 
    virtual void bar() { cout << "Base::bar" << endl; } 
    virtual ~Base() = default; 
}; 

class Child : public Base 
{ 
public: 
    void foo() { cout << "Child::foo" << endl; } 
    void bar() { cout << "Child::bar" << endl; } 
}; 

int main() 
{ 
    Base* obj = new Child; 
    obj->foo(); 
    obj->bar(); 
    delete obj; 
} 

https://gcc.godbolt.org/ gcc 5.3 ve çınlama 3.7 kullanılarak -O2 -std=c++11 ile derlenmiştir. çıktı ne

ne derleyici her şeyi optimize edebilmek olmasıdır - gcc inlines foo(), clang foo() için çağrı yapar ve devirtualizes ve inlines bar() çağrısı yaparken bar() sanal çağrı yapar. bunun yerine obj->bar(); ve sonra obj->foo(); ararsanız

arada, derleyiciler optimize sorunum yok - clang hem aramaları inlines ve gcc yerine sanal bir ve inline foo() ait bar() normal çağrı yapar.

Bu davranışı herkes açıklayabilir mi?

+0

bu soru garip. Neye cevap vermemiz gerekiyor? GCC Clang'dan daha mı kötü? bir zamanlar derleyiciler bir şeyler anlayabilirler, bazen özlerler. Clang daha yeni ve bu tür optimizasyonları desteklemek için sıfırdan inşa edildi. –

+0

Hayır, sanal olmayan çağrının önce yapılması durumunda, bu kötü optimizasyonun ötesinde özel bir şey olup olmadığını merak ediyorum. Devirtualizasyonu bozan bazı optimizasyonlar zaten yapıldı mı? – cailinscath

+1

http://hubicka.blogspot.de/2014/04/devirtualization-in-c-part-5-feedback.html gcc için ilginç arka plan bilgileri sağlar. Bu onu uygulayan gcc geliştiricisinden devirtualization hakkında makaleler ciddi bir bütündür. Gcc'e "-fwhole-program" veya "-fsuggest-final-method" eklemeyi denediniz mi? – Jens

cevap

5

Muhtemelen derleyici, satır içi yazmanın yardımcı olmadığına inanıyor çünkü cout, işlev çağrısının ek yüküne göre çok pahalı. Daha basit bir şeyle değiştirirseniz, ör. Bir üyeye bir ödev, inline olacaktır.

#include <iostream> 
using std::cout; 
using std::endl; 

class Base 
{ 
public: 
    void foo() { i = 1; } 
    virtual void bar() { i = 2; } 
    virtual ~Base() = default; 

    int i = 0; 
}; 

class Child : public Base 
{ 
public: 
    void foo() { i = 3; } 
    void bar() { i = 4; } 
}; 

int main() 
{ 
    Base* obj = new Child; 
    obj->foo(); 
    obj->bar(); 
    std::cout << obj->i << std::endl; 
    //delete obj; 
} 

Kurul çıkışı için aşağıya bakın:

Base::bar(): 
     movl $2, 8(%rdi) 
     ret 
Child::bar(): 
     movl $4, 8(%rdi) 
     ret 
Base::~Base(): 
     ret 
Child::~Child(): 
     ret 
Child::~Child(): 
     jmp  operator delete(void*) 
Base::~Base(): 
     jmp  operator delete(void*) 
main: 
     subq $8, %rsp 
     movl $16, %edi 
     call operator new(unsigned long) 
     movl $4, %esi 
     movl std::cout, %edi 
     call std::basic_ostream<char, std::char_traits<char> >::operator<<(int) 
     movq %rax, %rdi 
     call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&) 
     xorl %eax, %eax 
     addq $8, %rsp 
     ret 
     subq $8, %rsp 
     movl std::__ioinit, %edi 
     call std::ios_base::Init::Init() 
     movl $__dso_handle, %edx 
     movl std::__ioinit, %esi 
     movl std::ios_base::Init::~Init(), %edi 
     addq $8, %rsp 
     jmp  __cxa_atexit 
İlgili konular