2012-09-26 16 views
6

Quindi ho una classe base astratta senza metodi astratti. Per far valere astrattezza, ho dichiarato il (non banale) distruttore come pura virtuale:Eredità virtuale pura, ereditarietà multipla e C4505

class AbstractClass 
{ 
public: 
    AbstractClass() 
    { 
    std::wcout << L"AbstractClass::AbstractClass()" << std::endl; 
    } 
    virtual ~AbstractClass() = 0 
    { 
    std::wcout << L"AbstractClass::~AbstractClass()" << std::endl; 
    } 
}; 

class ConcreteClass : public AbstractClass 
{ 
public: 
    ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::ConcreteClass()" << std::endl; 
    } 
    virtual ~ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::~ConcreteClass()" << std::endl; 
    } 
}; 

Questo costruisce e funziona come previsto; l'uscita di un blocco di codice che definisce semplicemente un'istanza di ConcreteClass è

 

    AbstractClass::AbstractClass() 
    ConcreteClass::ConcreteClass() 
    ConcreteClass::~ConcreteClass() 
    AbstractClass::~AbstractClass() 

Ora, quando ho derivare AbstractClass da un'altra classe utilizzata come classe di interfaccia, si avente una (banale) distruttore virtuale (puro o altrimenti) , funziona ancora:

class IAlpha 
{ 
public: 
    virtual ~IAlpha() = 0 {} 
}; 

class AbstractClass : public IAlpha 
{ 
public: 
    AbstractClass() 
    { 
    std::wcout << L"AbstractClass::AbstractClass()" << std::endl; 
    } 
    virtual ~AbstractClass() = 0 
    { 
    std::wcout << L"AbstractClass::~AbstractClass()" << std::endl; 
    } 
}; 

class ConcreteClass : public AbstractClass 
{ 
public: 
    ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::ConcreteClass()" << std::endl; 
    } 
    virtual ~ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::~ConcreteClass()" << std::endl; 
    } 
}; 

Il problema sorge quando si tenta di implementare due interfacce differenti in questo modo:

class IAlpha 
{ 
public: 
    virtual ~IAlpha() = 0 {} 
}; 

class IBeta 
{ 
public: 
    virtual ~IBeta() = 0 {} 
}; 

class AbstractClass : public IAlpha, public IBeta 
{ 
public: 
    AbstractClass() 
    { 
    std::wcout << L"AbstractClass::AbstractClass()" << std::endl; 
    } 
    virtual ~AbstractClass() = 0 
    { 
    std::wcout << L"AbstractClass::~AbstractClass()" << std::endl; 
    } 
}; 

class ConcreteClass : public AbstractClass 
{ 
public: 
    ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::ConcreteClass()" << std::endl; 
    } 
    virtual ~ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::~ConcreteClass()" << std::endl; 
    } 
}; 

a questo punto, quando BU ilding, ricevo il seguente avviso:

warning C4505: 'AbstractClass::~AbstractClass' : 
unreferenced local function has been removed

Stranamente, però, l'output mostra ancora AbstractClass::~AbstractClass() sempre chiamato.

Si tratta di un bug in MSVC9 (VS 2008)? Posso tranquillamente ignorare questo avviso?

Modifica: ho provato a separare le definizioni del metodo virtuale puro anche dalla definizione della classe, in quanto apparentemente la sintassi = 0 {} non è effettivamente valida. Sfortunatamente, C4505 si presenta ancora, sia che io specifichi inline oppure no.

Poiché non ho trovato alcun modo di inviare questo avviso solo per questi metodi (l'avviso viene attivato da altre parti del codice), potrei dover rimuovere lo specificatore virtuale puro da AbstractClass e fare affidamento sui costruttori protetta. Non è una soluzione ideale, ma batte rearchitecting la gerarchia di classe per aggirare un avviso errato.

risposta

0

Hai provato a definire i distruttori in modo non in linea? Forse l'avvertimento è collegato a questo.

Come this code.

+0

Ho provato il codice nel tuo collegamento; riceve lo stesso avvertimento anche se, come prima, funziona correttamente durante l'esecuzione. – somethingdotjunk

0

Il codice non deve essere compilato. Le funzioni virtuali pure non possono essere definite all'interno della definizione della classe. Sposta la definizione all'esterno delle classi:

struct IAlpha { 
    virtual ~IAlpha() = 0; 
}; 
inline IAlpha::~IAlpha() {} 
// similarly for the rest. 

Oltre a ciò, il codice è corretto e deve essere compilato.

+0

Interessante; Non sapevo che non si potesse combinare il puro specificatore virtuale e il corpo del metodo. Sfortunatamente, l'ho provato anche in questo modo e l'avviso si presenta ancora (vedi risposta di jeffmagill). – somethingdotjunk

3

Questo è un bug in MSVC++ 2010 e versioni precedenti. Il codice riceve effettivamente il numero chiamato anche se il compilatore afferma di aver rimosso il codice. Sembra essere corretto in MSVC++ 2012. Altri compilatori come gcc o clang non emettono un avviso. La sintassi "... = 0 {...}" è illegale secondo la sezione 10.4 dello standard C++ 03.2 (anche se MSVC++ non si lamenta) come è già stato sottolineato:

Nota: una dichiarazione di funzione non può fornire sia una pura specificatore e una definizione

Tuttavia, definente un puro il distruttore virtuale in generale non è illegale e la sezione 12.4.7 afferma:

Un distruttore può essere dichiarato virtuale (10.3) o virtuale puro (10.4); se vengono creati oggetti di quella classe o classe derivata nel programma , deve essere definito il distruttore. Se una classe ha una classe base con un distruttore virtuale, il suo distruttore (se utente- o implicitamente dichiarato) è virtuale.

Il mio modo di disattivare l'avviso è quello di aggiungere le seguenti righe all'intestazione:

#if defined(_MSC_VER) && (_MSC_VER <= 1600) 
# pragma warning(disable:4505) 
#endif 

Se si desidera disabilitare gli avvertimenti di più a livello locale poi #pragma warning(push) e #pragma warning(pop) potrebbe essere di aiuto. Vedi http://msdn.microsoft.com/en-us/library/2c8f766e(v=vs.80).aspx

Poiché il codice sembra essere chiamato, è possibile ignorare gli avvisi a mio avviso.

+0

Sarei interessato a sapere dove è stato segnalato questo bug, quindi posso citarlo. – chappjc

0

Non è possibile definire la funzione virtuale in linea.

Poiché la riga è in fase di compilazione. Il virtuale è in runtime.