2014-12-17 23 views
6

Esiste una penalizzazione delle prestazioni quando un metodo virtuale viene chiamato da una classe che è nota per essere la classe derivata in fase di compilazione? Sotto ho chiamato esplicitamente force_speak con una classe derivata.Prestazioni delle funzioni virtuali chiamate da classi derivate?

Codice:

#include <iostream> 
#include <array> 
#include <memory> 

class Base 
{ 
public: 
    virtual void speak() 
    { 
    std::cout << "base" << std::endl; 
    } 
}; 

class Derived1 : public Base 
{ 
public: 
    void speak() 
    { 
    std::cout << "derived 1" << std::endl; 
    } 
}; 

template<class B> 
void force_speak(std::array<std::unique_ptr<B>, 3>& arr) 
{ 
    for (auto& b: arr) 
    { 
    b->speak(); 
    } 
} 

int main() 
{ 
    std::array<std::unique_ptr<Derived1>, 3> arr = 
    { 
     std::unique_ptr<Derived1>(new Derived1), 
     std::unique_ptr<Derived1>(new Derived1), 
     std::unique_ptr<Derived1>(new Derived1) 
    }; 
    force_speak(arr); 
    return 0; 
} 
+2

Si dovrebbe fare una domanda alla volta. – juanchopanza

+0

Che cos'è una penalizzazione delle prestazioni, qualcosa che pensi sia o qualcosa che hai misurato (o meglio riconosciuto come uno)? –

risposta

6

C'è una penalizzazione delle prestazioni quando un metodo virtuale viene chiamato da una classe che è noto per essere la classe derivata al momento della compilazione? Vedi il codice qui sotto.

Dipende. La maggior parte dei compilatori di codice "de-Virtualizzazione" come questo:

Derived1 d; 
d.speak(); 

Il tipo dinamico dell'oggetto è nota nel sito di chiamata, in modo che il compilatore può evitare di passare attraverso il vtable per effettuare la chiamata e può chiamare Derived1::speak() direttamente.

Nel tuo esempio il compilatore ha bisogno di essere più intelligente perché in force_speak hai solo Derived1* puntatori (deposito all'interno degli unique_ptr gli oggetti) e in quel contesto non è chiaro se il tipo dinamico del oggetti appuntiti-to è Derived1 o qualche altro tipo invecchiato Il compilatore deve sia in linea la chiamata a force_speak in main (dove è noto il tipo dinamico) o utilizzare alcune informazioni aggiuntive sul tipo per consentire la devirtualizzazione. (Come un esempio di conoscenze supplementari, l'ottimizzazione dell'intero programma è in grado di determinare che non ci sono altri tipi derivati ​​dichiarati in qualsiasi parte del programma, in modo un Derived1*must punto ad un Derived1.)

Utilizzando il C++ 11 La parola chiave final può aiutare i compilatori a ridimensionare alcuni casi, ad es. se Derived1 è segnato final poi il compilatore sa che un Derived1* può puntare a un Derived1 e non qualche altro tipo derivato da esso che potrebbe ignorare speak()

+4

Guardando all'assemblaggio, sembra che né GCC né Clang abbiano completamente devirtualizzato la chiamata nel codice OP. (GCC fa qualcosa di intelligente, inline 'Derived1 :: speak()' e fa un confronto sul puntatore vtable, saltando su una chiamata virtuale solo se il confronto non è uguale.) 'Final' su entrambe le classi o la funzione lo rende devirtualizzato . –

+0

@ T.C. C'è qualche necessità per il parametro template 'B' in' force_speak', cioè posso dichiarare 'force_speak' come' void force_speak (std :: array , 3> & arr) 'e ottenere sempre la virtualizzazione usando finale? Grazie. –

+1

@anon, non puoi farlo, 'array , N>' non è convertibile in 'array , N>' –

1

In questo caso specifico, la risposta è forse .

Se il compilatore decide di incorporare force_speak(), in teoria è possibile dedurre che tutti i puntatori nell'array sono istanze Derived1 e quindi chiamare il metodo in modo statico. (Questa ottimizzazione non è richiesta dallo standard, quindi se il metodo viene chiamato staticamente o virtualmente dipende dal particolare compilatore e possibilmente quali opzioni si utilizzano durante la compilazione.)

Se il compilatore non esegue la chiamata in linea, è necessario che sia emette le istruzioni per chiamare virtualmente il metodo, poiché è possibile derivare ulteriormente la classe Derived1 e sovrascrivere nuovamente il metodo, memorizzare le istanze di tale classe in std::unique_ptr<Derived1> e passare un array di quelle nella funzione.

2
  1. È dipendente dal compilatore. Il compilatore deve sapere staticamente che Derived1:speak() è l'unica opzione. Ciò include la consapevolezza che non esiste alcuna definizione Derived2::speak(), dove class Derived2: public Derived1. Ma il compilatore potrebbe non implementare affatto tale ottimizzazione.

  2. I parametri del modello di funzione possono essere dedotti automaticamente da un compilatore. Questo fa parte dello standard C++. Il compilatore conosce il tipo di parametro in un sito di chiamata in modo che l'utente non debba fornirlo. Nota che l'utente può fornire il tipo e che può ad esempio fornire un tipo compatibile ma diverso dal tipo di un parametro attuale.

+0

Grazie per aver risposto alla seconda domanda (ora rimossa). –