¿Cómo hacer el equivalente de memset (esto, …) sin golpear el vblbl?

Sé que memset está mal visto para class inicialización de class . Por ejemplo, algo como lo siguiente:

 class X { public: X() { memset( this, 0, sizeof(*this) ) ; } ... } ; 

vtbl el vtbl si hay una función virtual en la mezcla.

Estoy trabajando en una base de código heredada (descomunal) que es C-ish pero comstackda en C ++, por lo que todos los miembros en cuestión son típicamente POD y no requieren constructores C ++ tradicionales. El uso de C ++ se arrastra gradualmente (como las funciones virtuales), y esto muerde a los desarrolladores que no se dan cuenta de que memset tiene estos dientes C ++ adicionales.

Me pregunto si hay una forma segura de C ++ para hacer una inicialización inicial catch-all cero, que podría ser seguida por una inicialización específica por miembro donde la inicialización cero no es apropiada.

Encuentro las preguntas similares memset para la inicialización en C ++ y la estructura derivada a cero usando memset . Ambos tienen respuestas “do not use memset ()”, pero no hay buenas alternativas (especialmente para estructuras grandes que potencialmente contienen muchos miembros).

Para cada clase donde encuentre una llamada memset , agregue una función memset member que ignore los argumentos de puntero y tamaño y memset asignaciones a todos los miembros de datos.

editar: en realidad, no debería ignorar el puntero, debería compararlo con this . En una coincidencia, haga lo correcto para que el objeto, en un desajuste, vuelva a enrutar a la función global.

Siempre puede agregar constructores a estas estructuras incrustadas, por lo que se borran por así decirlo.

Prueba esto:

 template  void reset(T& t) { t = T(); } 

Esto pondrá a cero tu objeto, no importa si es POD o no.

Pero no hagas esto:

  A::A() { reset(*this); } 

¡Esto invocará a A::A en recursión infinita!

Prueba esto:

  struct AData { ... all A members }; class A { public: A() { reset(data); } private: AData data; }; 

Esto es horrible, pero podría sobrecargar el operator new/delete para estos objetos (o en una clase base común) y hacer que la implementación proporcione buffers sin salida. Algo como esto :

 class HideousBaseClass { public: void* operator new( size_t nSize ) { void* p = malloc( nSize ); memset( p, 0, nSize ); return p; } void operator delete( void* p ) { if( p ) free( p ); } }; 

También se podrían anular los operadores globales new/delete , pero esto podría tener implicaciones de rendimiento negativas.

Editar: Me acabo de dar cuenta de que este enfoque no funcionará para los objetos asignados a la stack.

Aproveche el hecho de que una instancia estática se inicializa a cero: https://ideone.com/GEFKG0

 template  struct clearable { void clear() { static T _clear; *((T*)this) = _clear; }; }; class test : public clearable { public: int a; }; int main() { test _test; _test.a=3; _test.clear(); printf("%d", _test.a); return 0; } 

Sin embargo, lo anterior causará que el constructor (de la clase de la clase) sea llamado por segunda vez.

Para una solución que no causa ninguna llamada de ctor, puede usarla en su lugar: https://ideone.com/qTO6ka

 template  struct clearable { void *cleared; clearable():cleared(calloc(sizeof(T), 1)) {} void clear() { *((T*)this) = *((T*)cleared); }; }; 

… y si usa C ++ 11 en adelante, puede usar lo siguiente: https://ideone.com/S1ae8G

 template  struct clearable { void clear() { *((T*)this) = {}; }; }; 

La mejor solución que pude encontrar es crear una estructura separada donde pondrás a cero los miembros que deben configurarse a cero. No estoy seguro de si este diseño es adecuado para usted.

Esta estructura no tiene vtable y no se extiende nada. Será solo un pedazo de información. De esta manera memsetting la estructura es seguro.

He hecho un ejemplo:

 #include  #include  struct X_c_stuff { X_c_stuff() { memset(this,0,sizeof(this)); } int cMember; }; class X : private X_c_stuff{ public: X() : normalMember(3) { std::cout << cMember << normalMember << std::endl; } private: int normalMember; }; int main() { X a; return 0; } 

Puede usar la aritmética del puntero para encontrar el rango de bytes que desea poner a cero:

 class Thing { public: Thing() { memset(&data1, 0, (char*)&lastdata - (char*)&data1 + sizeof(lastdata)); } private: int data1; int data2; int data3; // ... int lastdata; }; 

(Editar: Originalmente usé offsetof() para esto, pero un comentario señaló que se supone que esto solo funciona en PODs, y luego me di cuenta de que puedes usar las direcciones de los miembros directamente).