我从一个非常简单的程序开始:
#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 :: 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,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::string
或std::vector
,我想修复此功能。
答案 0 :(得分:0)
执行赋值后,您正在泄漏构造函数中初始化的所有字节。要调试泄漏,请在执行分配时逐步调试。在m_Data
变量上设置观察点可能很有用,这样调试器就会在更改值时停止。
答案 1 :(得分: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
的一个版本...
喔。
哦等一下。
哦,该死的。
这最好不是我认为的。
Project Toolbox -> $(OutDir)\$(ProjectName)_d.lib
我要谋杀那个花了三个小时的人。
Project Game -> Toolbox.lib
等等哦。那是我。
TL; DR:我链接到字符串类的旧版本,导致各种奇怪的行为,包括泄漏内存。