Конфликты скрытого менеджера памяти bds 2006 C (класс new / delete[] и AnsiString)
Я давно использую BDS 2006 Turbo C++, и некоторые из моих более крупных проектов (CAD / CAM, 3D- движки gfx и астрономические вычисления) иногда выдают исключение (например, раз в 3-12 месяцев при работе в тяжелых условиях 24/7).). После обширной отладки я нашел это:
//code1:
struct _s { int i; } // any struct
_s *s=new _s[1024]; // dynamic allocation
delete[] s; // free up memory
этот код обычно находится внутри шаблона, где _s
может быть также классом, поэтому delete[]
этот код должен работать правильно, но delete[]
не работает должным образом для структур (классы выглядят нормально). Никаких исключений не выдается, память освобождается, но это каким-то образом повреждает таблицы распределения диспетчера памяти, и после этого любое новое распределение может быть неправильным (new может создавать перекрывающиеся распределения с уже выделенным пространством или даже нераспределенным пространством, следовательно, случайные исключения)
Я обнаружил, что если я добавлю пустой деструктор в _s
чем вдруг кажется все ок
struct _s { int i; ~_s(){}; }
Что ж, теперь начинается странная часть. После того, как я обновлю это для своих проектов, я обнаружил, что AnsiString
класс также имеет плохие перераспределения. Например:
//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;
В этом коде dat
содержит некоторые полезные данные, а затем некоторые txt
строка, созданная путем добавления строк, чтобы txt
должны быть перераспределены несколько раз, а иногда dat
данные перезаписываются txt
(даже если они не перекрываются, я думаю, что температура AnsiString
необходимо перераспределить txt
перекрывается с dat
)
Итак, мои вопросы:
- Я делаю что-то не так в code1, code2?
Есть ли способ избежать
AnsiString
(пере) ошибки распределения? (но все еще использую это)- После обширной отладки (после публикации вопроса 2) я обнаружил, что
AnsiString
не вызывает проблем. Они появляются только при их использовании. Настоящая проблема, вероятно, заключается в переключении между клиентами OpenGL. У меня есть диалоги открытия / сохранения с предварительным просмотром для векторной графики. Если я отключу использование OpenGL для этих подокнов VCL, чемAnsiString
Ошибки управления памятью исчезают полностью. Я не знаю, в чем проблема (несовместимость между окнами MFC/VCL или, скорее всего, я допустил какую-то ошибку при переключении контекста, рассмотрим подробнее). Концерны OpenGL имеют следующие окна: - основная форма VCL + OpenGL внутри
Canvas
клиентская зона - дочерний элемент основного диалогового окна открытия / сохранения MFC + закрепленный предварительный просмотр формы VCL + OpenGL внутри
Canvas
клиентская зона
- После обширной отладки (после публикации вопроса 2) я обнаружил, что
PS
- эти ошибки зависят от количества
new/delete/delete[]
использования не по выделенным размерам - и ошибки code1, и code2 являются повторяющимися (например, имеется синтаксический анализатор для загрузки сложного INI-файла, и ошибка возникает в одной строке, если INI-файл не изменяется)
- Я обнаруживаю эти ошибки только в больших проектах (простой исходный код> 1 МБ) с комбинированным использованием
AnsiString
и шаблоны с внутренним динамическим распределением, но возможно, что они также находятся в более простых проектах, но встречаются настолько редко, что я пропускаю это. - Спецификации зараженных проектов:
- win32 noinstall standalone (при использовании Win7sp1 x64, но на XPsp3 x32 ведет себя так же)
- не измеряет, если использовать GDI или OpenGl/GLSL
- не измеряет, если использовать DLL драйвера устройства или нет
- нет OCX или нестандартного компонента VCL
- нет DirectX
- Компиляция / ссылка на 1 байт
- не используйте RTL, пакеты или фреймворки (автономно)
Извините за плохой английский / грамматику... любая помощь / заключение / предложение приветствуется.
1 ответ
После обширной отладки я точно изолировал проблему. Управление памятью в bds2006 Turbo C++ стало поврежденным после попытки вызвать любое удаление для уже удаленного указателя. например:
BYTE *dat=new BYTE[10],*tmp=dat;
delete[] dat;
delete[] tmp;
После этого управление памятью ненадежно. ("новый" может выделить уже выделенное пространство)
Конечно, удаление одного и того же указателя дважды является ошибкой на стороне программистов, но я нашел реальную причину всех моих проблем, которая порождает эту проблему (без какой-либо явной ошибки в исходном коде), см. Этот код:
//---------------------------------------------------------------------------
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();
}
//---------------------------------------------------------------------------
В функции get()
называется деструктором для класса дважды. Один раз для реального и один раз для его копии, потому что я забыл создать конструктор
test::test(test &x);
[Edit1] дальнейшие обновления кода
Хорошо, я усовершенствовал код инициализации для шаблонов классов и даже структур, чтобы исправить еще больше ошибок. Добавьте этот код в любую структуру / класс / шаблон и, если необходимо, добавьте функциональность.
T() {}
T(T& a) { *this=a; }
~T() {}
T* operator = (const T *a) { *this=*a; return this; }
//T* operator = (const T &a) { ...copy... return this; }
T
это имя структуры / класса- последний оператор нужен только если
T
использует динамическое распределение внутри него, если оно не используется, вы можете оставить его как есть
Это также решает другие проблемы компилятора как это:
Если у кого-то есть подобные проблемы, надеюсь, это поможет.
Также посмотрите на traceback указатель в коде C++mmap
если вам нужно отладить распределение памяти...