In una base di codice complesso, ho un array di puntatore classe base non virtuale (la classe base non ha metodi virtuali)C++ pericoloso Cast soluzione
Considerate questo codice:
#include <iostream>
using namespace std;
class TBase
{
public:
TBase(int i = 0) : m_iData(i) {}
~TBase(void) {}
void Print(void) {std::cout << "Data = " << m_iData << std::endl;}
protected:
int m_iData;
};
class TStaticDerived : public TBase
{
public:
TStaticDerived(void) : TBase(1) {}
~TStaticDerived(void) {}
};
class TVirtualDerived : public TBase
{
public:
TVirtualDerived(void) : TBase(2) {}
virtual ~TVirtualDerived(void) {} //will force the creation of a VTABLE
};
void PrintType(TBase *pBase)
{
pBase->Print();
}
void PrintType(void** pArray, size_t iSize)
{
for(size_t i = 0; i < iSize; i++)
{
TBase *pBase = (TBase*) pArray[i];
pBase->Print();
}
}
int main()
{
TBase b(0);
TStaticDerived sd;
TVirtualDerived vd;
PrintType(&b);
PrintType(&sd);
PrintType(&vd); //OK
void* vArray[3];
vArray[0] = &b;
vArray[1] = &sd;
vArray[2] = &vd; //VTABLE not taken into account -> pointer not OK
PrintType(vArray, 3);
return 0;
}
L'uscita è (compilato con Mingw-W64 GCC 4.9.2 on Win64):
Data = 0
Data = 1
Data = 2
Data = 0
Data = 1
Data = 4771632
la ragione del fallimento è che ogni istanza di TVirtualDerived ha un puntatore alla tabella virtuale, che non ha le TBase. Quindi, risalire fino a TBase senza informazioni di tipo precedenti (da void * a TBase *) non è sicuro.
Il fatto è che non posso evitare di lanciare a vuoto * in primo luogo. Aggiunta di un metodo virtuale (distruttore per esempio) sulle opere classe di base, ma ad un costo di memoria (che voglio evitare)
Contesto:
stiamo implementando un sistema di segnale/scanalatura, in modo molto ambiente limitato (memoria severamente limitata). Dal momento che abbiamo diversi milioni di oggetto che può inviare o ricevere segnali, questo tipo di ottimizzazione è efficace (quando funziona, ovviamente)
Domanda:
Come posso risolvere questo problema? Finora, ho trovato:
1 - aggiungi un metodo virtuale in TBase. Funziona, ma in realtà non risolve il problema, lo evita. Ed è inefficiente (troppa memoria)
2 - trasmettere a TBase * invece di trasmettere a vuoto * nell'array, a spese di una perdita di generalità. (probabilmente quello che proverò dopo)
Vedete un'altra soluzione?
Semplicemente il casting in 'TBase *' * prima * il casting in 'void *' risolve il problema in modo soddisfacente? ([Vedi qui] (https : //ideone.com/kpNhe6)) –
Giusto per essere chiari: alcune delle tue classi derivate hanno metodi virtuali. Altri no. E TBase stesso è così piccolo che l'aggiunta di un puntatore vtable causa un significativo aumento delle dimensioni della memoria. Corretta? –
Hai considerato l'utilizzo di modelli? – cup