2015-05-18 9 views
7

Ho riscontrato un caso strano quando la parola chiave final viene aggiunta a una dichiarazione di funzione virtuale, con la sua definizione su un file .cpp separato.
consideri il seguente esempio:Comportamento anomalo del finale su una funzione virtuale

IClass.hpp

class IClass //COM-like base interface 
{ 
protected: 
    virtual ~IClass(){} //derived classes override this 

public: 
    virtual void release() final; 
}; 

dllmain.cpp (libreria condivisa)

#include "IClass.hpp" 
... 

void IClass::release() 
{ 
    delete this; 
} 

... 

main.cpp (eseguibile standalone)

//various includes here 
... 

int main(int argc, char** argv) 
{ 
    /* From "IGameEngine.hpp" 
     class IGameEngine : public IClass 
     { 
     ... 
     }; 
    */ 
    IGameEngine* engine = factoryGameEngine(); 
    ... 
    engine->release(); 
    return 0; 
} 

Così com'è, GCC 4.9.2 segnala un undefined reference to 'IClass::release()'
Il mio obiettivo è quello di avere IClass::release() come non-override pur avendo la sua attuazione nascosto all'interno libreria condivisa del motore di gioco.
Qualche suggerimento?

+0

Le funzioni virtuali vengono sempre utilizzate odr a meno che non siano puri. Credo che il linker sia autorizzato a emettere un errore in quel caso. – 0x499602D2

+1

Non riesco a riprodurre il problema sul mio gcc 4.9.2. Per favore aggiungi come stai costruendo il tuo programma (opzioni del compilatore, ecc.) – dyp

+1

Hai esportato la funzione dalla dll? @ 0x49 Avevo l'illusione che la loro odr-used-ness fosse definita dall'implementazione. – Yakk

risposta

2

Ha fatto alcuni scavi per l'utilizzo di GCC di final e risulta funzioni virtuali contrassegnate come get finale "devirtualized", una fase di ottimizzazione che mira ad accelerare le chiamate virtuali mediante l'invio statico e possibilmente inlining.

Questo spiega l'errore del linker, poiché tenta di collegare IClass::release() nell'eseguibile ma non riesce a trovarlo localmente.

Questo comportamento "devirtualization" appare anche il clang, ma improbabile che accada con MSVC++


Parzialmente relativo suggerimento

Nel caso in cui avete bisogno di un modo per rilasciare un oggetto tramite un puntatore alla sua classe astratta (o classe base astratta):

  • La classe base astratta ha bisogno di un distruttore puramente virtuale
  • Fornire il distruttore definizione di default 's di fuori della classe (ambito vuoto)
  • Implementare il distruttore su tutte le classi derivate, come al solito

  • E se siete anche trattare con le librerie condivise:

  • Export un paio di Malloc/Free funzioni della libreria
  • Override del non-allineamento nuova/cancellare gli operatori e le loro rispettive versioni std::nothrow sul file di intestazione della tua biblioteca
  • chiamata sopra Malloc/libero da parte degli operatori sovrascritto

  • Poiché le implementazioni dell'interfaccia risiedono all'interno della libreria, esportare una funzione di fabbrica per ogni interfaccia che si ritiene costruibile dal cliente.
    Assicurati solo che le eccezioni non vengano propagate attraverso il divario tra client e libreria.

    In questo modo, l'applicazione client può utilizzare delete su un oggetto assegnato dal CRT della libreria, senza problemi.