Conflictos del administrador de memoria oculta de bds 2006 C (clase nueva/eliminar[] frente a AnsiString)
Estoy usando BDS 2006 Turbo C++ desde hace mucho tiempo y algunos de mis proyectos más importantes ( CAD/CAM, motores 3D gfx y cálculos astronómicos) ocasionalmente arrojan una excepción (por ejemplo, una vez cada 3 a 12 meses de uso intensivo las 24 horas, los 7 días de la semana). ). Después de una extensa depuración encontré esto:
//code1:
struct _s { int i; } // any struct
_s *s=new _s[1024]; // dynamic allocation
delete[] s; // free up memory
este código generalmente está dentro de la plantilla donde _s
también puede ser una clase, por lo tanto, delete[]
este código debería funcionar correctamente, pero delete[]
no funciona correctamente para las estructuras (las clases se ven bien). No se lanzan excepciones, la memoria se libera, pero de alguna manera daña las tablas de asignación del administrador de memoria y después de esto, cualquier nueva asignación puede ser incorrecta (las nuevas pueden crear asignaciones superpuestas con espacio ya asignado o incluso con espacio no asignado, de ahí las excepciones ocasionales)
Descubrí que si agrego un destructor vacío, _s
de repente todo parece estar bien.
struct _s { int i; ~_s(){}; }
Bueno, ahora viene la parte rara. Después de actualizar esto en mis proyectos, descubrí que AnsiString
la clase también tiene malas reasignaciones. Por ejemplo:
//code2:
int i;
_s *dat=new _s[1024];
AnsiString txt="";
// setting of dat
for (i=0;i<1024;i++) txt+="bla bla bla\r\n";
// usage of dat
delete[] dat;
Este código dat
contiene algunos datos útiles, luego se txt
crea una cadena agregando líneas, por lo que se txt
deben reasignar varias veces y, a veces, los dat
datos se sobrescriben txt
(incluso si no están superpuestos, creo que la temperatura AnsiString
necesaria para reasignar txt
se superpone con dat
)
Entonces mis preguntas son:
- ¿Estoy haciendo algo mal en código1, código2?
¿ Hay alguna forma de evitar
AnsiString
errores de (re)asignación? (pero sigo usándolo)- Después de una depuración exhaustiva (después de publicar la pregunta 2), descubrí que
AnsiString
no causan problemas. Sólo ocurren mientras los usamos. El verdadero problema probablemente esté en cambiar entre clientes OpenGL . Tengo cuadros de diálogo Abrir/Guardar con vista previa para gráficos vectoriales. Si desactivo el uso de OpenGL para estas subventanas de VCLAnsiString
, los errores de administración de memoria desaparecen por completo. No sé cuál es el problema (incompatibilidad entre ventanas MFC/VCL o, más probablemente, cometí algún error al cambiar de contexto, lo investigaré más a fondo). Las ventanas OpenGL que preocupan son: - Formulario VCL principal + OpenGL dentro del
Canvas
área del cliente - hijo del cuadro de diálogo principal Abrir/Guardar de MFC + formulario VCL de vista previa acoplado + OpenGL dentro del
Canvas
área del cliente
- Después de una depuración exhaustiva (después de publicar la pregunta 2), descubrí que
PD
- Estos errores dependen del número de
new/delete/delete[]
usos, no de los tamaños asignados. - Tanto los errores de código1 como de código2 son repetitivos (por ejemplo, tenga un analizador para cargar un archivo ini complejo y el error ocurre en la misma línea si no se cambia el ini)
- Detecto estos errores solo en proyectos grandes (código fuente simple > 1 MB) con uso combinado de
AnsiString
plantillas con asignaciones dinámicas internas, pero es posible que también estén en proyectos más simples, pero ocurren tan raramente que los pierdo. - Especificaciones de proyectos infectados:
- win32 noinstall independiente (usando Win7sp1 x64 pero en XPsp3 x32 se comporta igual)
- no mide si usa GDI u OpenGl/GLSL
- no mide si usa el controlador de dispositivo DLL o no
- sin OCX o componente VCL no estándar
- sin DirectX
- Compilación/enlace alineado de 1 byte
- no utilice RTL , paquetes o marcos (independientes)
Perdón por el mal inglés/gramática... se agradece cualquier ayuda/conclusión/sugerencia.
Después de una depuración exhaustiva, aislé finamente el problema. La administración de memoria de bds2006 Turbo C++ se corrompió después de intentar ejecutar cualquier eliminación para un puntero ya eliminado. Por ejemplo:
BYTE *dat=new BYTE[10],*tmp=dat;
delete[] dat;
delete[] tmp;
Después de esto, la gestión de la memoria no es confiable. ('nuevo' puede asignar espacio ya asignado)
Por supuesto, eliminar el mismo puntero dos veces es un error por parte de los programadores, pero he encontrado la causa real de todos mis problemas que genera este problema (sin ningún error obvio en el código fuente). Vea este código:
//---------------------------------------------------------------------------
class test
{
public:
int siz;
BYTE *dat;
test()
{
siz=10;
dat=new BYTE[siz];
}
~test()
{
delete[] dat; // <- add breakpoint here
siz=0;
dat=NULL;
}
test& operator = (const test& x)
{
int i;
for (i=0;i<siz;i++) if (i<x.siz) dat[i]=x.dat[i];
for ( ;i<siz;i++) dat[i]=0;
return *this;
}
};
//---------------------------------------------------------------------------
test get()
{
test a;
return a; // here call a.~test();
} // here second call a.~test();
//---------------------------------------------------------------------------
void main()
{
get();
}
//---------------------------------------------------------------------------
En función get()
se llama destructor para la clase a dos veces. Una vez de verdad y otra para su copia porque me olvidé de crear el constructor.
test::test(test &x);
[Editar1] actualizaciones adicionales del código
OK, he refinado el código de inicialización para las plantillas de clase y estructura, incluso para corregir aún más casos de errores. Agregue este código a cualquier estructura/clase/plantilla y, si es necesario, agregue funcionalidad
T() {}
T(const T& a) { *this=a; }
~T() {}
T* operator = (const T *a) { *this=*a; return this; }
//T* operator = (const T &a) { ...copy... return this; }
T
es el nombre de la estructura/clase- el último operador es necesario solo si
T
usa asignaciones dinámicas dentro de él, si no se usan asignaciones, puede dejarlo como está
Esto también resuelve otros problemas del compilador como este:
- Error de demasiados inicializadores para una matriz simple en bcc32
Si alguien tiene problemas similares, espero que esto ayude.
También mire el rastreo de un puntero en el código C++mmap
si necesita depurar sus asignaciones de memoria...