2012-06-12 3 views
3

я начинаю с очень простой программой:Изменение размера динамической строки вызывает утечку памяти

#include <TBString.h> 

int main(int argv, char** argc) 
{ 
    tb::String test(""); 
    test = "Hello World!"; 

    return 0; 
} 

tb::String мой собственный класс строки, которая была разработана для обработки как char строк и wchar_t (Unicode) строк. Он сильно замаскирован, tb::String - это typedef tb::StringBase<char>.

Все это скомпилировано с использованием утилит для отладки CRT для проверки утечек памяти. Вот результат:

Detected memory leaks! 
Dumping objects -> 
c:\users\sam\svn\dependencies\toolbox\headers\tbstring2.inl(38) : {442} normal block at 0x00D78290, 1 bytes long. 
Data: < > 00 
{131} normal block at 0x00C5EFA0, 52 bytes long. 
Data: <    > A0 EF C5 00 A0 EF C5 00 A0 EF C5 00 CD CD CD CD 
Object dump complete. 
Detected memory leaks! 
Dumping objects -> 
c:\users\sam\svn\dependencies\toolbox\headers\tbstring2.inl(38) : {442} normal block at 0x00D78290, 1 bytes long. 
Data: < > 00 
Object dump complete. 
The program '[2888] SAM_release.exe: Native' has exited with code 0 (0x0). 

Так это выглядит как пустой Тб :: String (с размером 0) вызывает утечку памяти. Подтвержденные с этой программой, которая не утечка:

#include <TBString.h> 

int main(int argv, char** argc) 
{ 
    tb::String test("Hello World!"); 

    return 0; 
} 

стек вызовов для оригинальной программы:

  • Создайте StringBase<char> шпагатом "".
  • m_Length устанавливается в 0.
  • m_Maximum установлен в m_Length + 1 (1).
  • m_Data создан с длиной m_Maximum (1).
  • m_Data очищается и заполняется "".
  • _AppendSingle установлен в StringBase<char>::_AppendDynSingle.
  • Перегруженный оператор StringBase<char>::operator = называется бечевкой "Hello World!"
  • _AppendSingle называется.
  • m_Length равно 0, равно 1. m_Maximum
  • checklen установлен в m_Length + src_len + 1 (13).
  • m_Maximum умножается на 2, пока оно больше, чем checklen (16).
  • Функция StringBase<char>::Resize вызывается с новым максимумом.

Resize функция:

template <typename C> 
TB_INLINE StringBase<C>& StringBase<C>::Resize(int a_Maximum /*= -1*/) 
{ 
    if (!m_Data) 
    { 
     m_Maximum = (a_Maximum == -1) ? 4 : a_Maximum; 
     m_Data = new C[m_Maximum]; 
     StringHelper::Clear<C>(m_Data, m_Maximum); 
    } 
    else 
    { 
     int newmax = (a_Maximum == -1) ? (m_Maximum * 2) : a_Maximum; 

     C* temp = new C[newmax]; 
     StringHelper::Clear<C>(temp, newmax); 
     if (m_Length > 0) { StringHelper::Copy(temp, m_Data, m_Length); } 
     delete [] m_Data; 
     m_Data = temp; 

     m_Maximum = newmax; 
    } 

    return *this; 
} 

Это то, что я подозреваю, что это проблема. Теперь, мой вопрос будет:

Как я могу перераспределить память на C++ без его запуска утечки памяти в отладчике CRT?

Конструктор:

TB_INLINE StringBase<char>::StringBase(const char* a_String) 
{ 
    m_Length = StringHelper::GetLength<char>(a_String); 
    m_Maximum = m_Length + 1; 
    m_Data = new char[m_Maximum]; 
    StringHelper::Clear<char>(m_Data, m_Maximum); 

    StringHelper::Copy<char, char>(m_Data, a_String, m_Length); 

    _AppendSingle = &StringBase<char>::_AppendDynSingle; 
    _AppendDouble = &StringBase<char>::_AppendDynDouble; 
} 

деструктор:

TB_INLINE StringBase<char>::~StringBase() 
{ 
    if (m_Data) { delete [] m_Data; } 
} 

Назначение Оператор:

TB_INLINE StringBase<char>& StringBase<char>::operator = (const char *a_String) 
{ 
    Clear(); 
    return (this->*_AppendSingle)(a_String); 
} 

Дозапись функции:

template<> 
TB_INLINE StringBase<char>& StringBase<char>::_AppendDynSingle(const char* a_String) 
{ 
    if (!a_String) { return *this; } 

    int src_len = StringHelper::GetLength<char>(a_String); 

    // check size 

    if (m_Maximum == -1) 
    { 
     m_Maximum = src_len + 1; 
     m_Data = new char[m_Maximum]; 
     StringHelper::Clear<char>(m_Data, m_Maximum); 
     m_Length = 0; 
    } 

    int checklen = m_Length + src_len + 1; 
    if (checklen > m_Maximum) 
    { 
     while (checklen > m_Maximum) { m_Maximum *= 2; } 
     Resize(m_Maximum); 
    } 

    // append 

    strcat(m_Data, a_String); 

    // new length 

    m_Length += src_len; 

    return *this; 
} 

Обратите внимание: я не хочу использовать std::string или std::vector, я хочу исправить эту функцию.

+1

Этот вопрос, в основном, относится к классу 'tb :: String'. Это один из ваших собственных сейчас или из какой-то библиотеки? – leftaroundabout

+0

Что такое 'tb :: String'? Что-то серьезное, или что-то, что вы взломали? –

+0

'tb :: String' - мой собственный динамический класс строк. Я могу опубликовать полный источник, если потребуется, но я считаю, что я вставил наиболее важные биты. – knight666

ответ

0

Это будет длинный.

Во-первых, я решил проверить свое здравомыслие. Правильно ли работает отладчик памяти CRT?

int* src_test = new int[10]; 
for (int i = 0; i < 10; i++) { src_test[i] = i; } 
int* dst_test = new int[10]; 
for (int i = 0; i < 10; i++) { dst_test[i] = src_test[i]; } 
delete [] src_test; 

Это правильно сообщает об утечке 40 байт. Эта линия фиксирует утечку:

delete [] dst_test; 

Хорошо, что еще? Ну, может быть, деконструктор не называется. Давайте добавим его в функцию:

void ScopeTest() 
{ 
    tb::String test("Hello World!"); 
    test = "Hello World! Again!"; 
} 

Это работает, но оно течет. Давайте абсолютно уверены, что деконструктор называется.

void ScopeTest() 
{ 
    tb::String* test = new tb::String("Hello World!"); 
    *test = "Hello World! Again!"; 
    delete test; 
} 

По-прежнему протекает. Ну, что делает оператор =? Он очищается и добавляется. Давайте сделаем это вручную:

void ScopeTest() 
{ 
    tb::String* test = new tb::String("Hello World!"); 
    test->Clear(); 
    test->Append("Hello World! Again!"); 
    delete test; 
} 

Тот же результат, поэтому он не имеет никакого отношения к оператору. Интересно, что бы произошло, если бы я удалил Clear ...

void ScopeTest() 
{ 
    tb::String* test = new tb::String("Hello World!"); 
    test->Append("Hello World! Again!"); 
    delete test; 
} 

Хорошо, это ... ждать, что? Это не утечка? Что делает Clear?

template <> 
TB_INLINE StringBase<char>& StringBase<char>::Clear() 
{ 
    if (m_Data) 
    { 
     StringHelper::Clear<char>(m_Data, m_Maximum); 
    } 

    m_Length = 0; 

    return *this; 
} 

Это ... безвредно. Но давайте прокомментируем это.

template <> 
TB_INLINE StringBase<char>& StringBase<char>::Clear() 
{ 
    /*if (m_Data) 
    { 
     StringHelper::Clear<char>(m_Data, m_Maximum); 
    } 

    m_Length = 0;*/ 

    return *this; 
} 

То же самое, никаких утечек. Давайте снова удалим вызов Clear.

void ScopeTest() 
{ 
    tb::String* test = new tb::String("Hello World!"); 
    //test->Clear(); 
    test->Append("Hello World! Again!"); 
    delete test; 
} 

неплотные байт снова ...

Но ждать второй, она по-прежнему очистки tb::String? Длина установлена ​​в 0, и данные обнуляются, даже если тело закомментировано. Как это...

Хорошо, компилятор, давайте посмотрим компиляции этот:

/*template <> 
TB_INLINE StringBase<char>& StringBase<char>::Clear() 
{ 
    if (m_Data) 
    { 
     StringHelper::Clear<char>(m_Data, m_Maximum); 
    } 

    m_Length = 0; 

    return *this; 
}*/ 

Ха! Это покажет ему! О, подождите ... он ... все еще компилируется и запускается.

Я использую другую версию того же файла? Нет, у меня есть только одна версия TBString2.h и TBString2.inl на этом компьютере ...

Oh.

О, подождите секунду.

О, черт возьми.

Этого лучше не быть, как я думаю.

Project Toolbox -> $(OutDir)\$(ProjectName)_d.lib 

Я собираюсь убить человека, который потратил на это три часа.

Project Game -> Toolbox.lib 

О, подождите. Это был я.

TL; DR: Я связан со старой сборкой класса строк, вызывая все виды странного поведения, включая утечку памяти.

0

Вы пропустите все байты, инициализированные в конструкторе после выполнения задания. Чтобы отладить утечку, перейдите к отладчику при выполнении задания. Возможно, было бы полезно установить точку наблюдения в переменной m_Data, чтобы отладчик остановился всякий раз, когда он меняет значение.

Смежные вопросы