2012-06-12 68 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是一個tb::StringBase<char>的typedef。

整個事情是使用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). 

因此,它看起來像一個空TB ::字符串(大小爲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,m_Maximum是1
  • checklen設置爲m_Length + src_len + 1(13)。
  • m_Maximum乘以2直到它大於checklen(16)。
  • StringBase<char>::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::stringstd::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.hTBString2.inl的一個版本...

哦。哦,等一下。哦,該死的。

這最好不是我認爲的。

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

我要謀殺在這個上花了三個小時的人。

Project Game -> Toolbox.lib 

哦等等。那是我。

TL; DR:我鏈接到一箇舊的字符串類構建,導致各種奇怪的行爲,包括泄漏的內存。

0

在執行任務後,您正在泄漏在構造函數中初始化的任何字節。要調試泄漏,請在執行分配時使用調試器。在m_Data變量上設置一個觀察點可能很有用,以便調試器在其值發生更改時停止。