0

我正在使用_CrtDumpMemoryLeaks(); 从 stdlib.h 和 crtdbg.h 来检测内存泄漏,但我注意到我的代码中有一些奇怪的地方。

如果我做:

int _tmain(int argc, _TCHAR* argv[])
{
    MyClass* myClass = new MyClass();
    _CrtDumpMemoryLeaks(); //I get a memory leak warning
}

但是,如果我这样做:

class MyClass
{
    public:
        char* NewChar();
};
char* MyClass::NewChar()
{
    char* test = new char[100];
    return test;
}

MyClass myClass; //Globally declared
int _tmain(int argc, _TCHAR* argv[])
{        
    char* charPointer = myClass.NewChar();
    _CrtDumpMemoryLeaks(); //No warnings
}

自从我的程序退出(就在_CrtDumpMemoryLeaks()之后)并且仍然有一个未删除的新字符后,我不应该收到警告吗?

如何检测这些内存泄漏?

另外,使用上面的相同示例,如果我添加代码:

char* anotherPointer = charPointer; //previously filled
delete[] anotherPointer;

这会从类内部删除新的字符以防止内存泄漏,还是我也应该在charPointer上调用 delete ?

4

4 回答 4

2

如果您想捕获(或有机会捕获)全局对象泄漏,请尝试在 main 开始时设置 CRT 调试标志_CRTDBG_LEAK_CHECK_DF。该标志强制在全局析构函数之后转储检测到的泄漏。

要回答您的问题:

自从我的程序退出(就在_CrtDumpMemoryLeaks()之后)并且仍然有一个未删除的新字符后,我不应该收到警告吗?

定义“警告”。调用_CrtDumpMemoryLeaks()应该转储当时未完成的任何内容。除非配置为这样做,否则之后退出的程序将不会进行另一次转储。

如何检测这些内存泄漏?

如果您使用的是调试 CRT 并正确_Crt配置了它们,它们应该被正确检测到,这主要默认情况下。


以下代码将_Crt转储系统设置为按需转储所有对象,并在最终退出之前(main()完成后并销毁全局静态数据)。

class MyLeak
{
public:
    MyLeak() { new unsigned char[1024]; }

    char * NewChar() { return new char[1024]; }
};

MyLeak myLeak;
int main(int argc, char *argv[])
{
    int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
    tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
    _CrtSetDbgFlag(tmpFlag);

    _CrtMemState ms = {0};
    _CrtMemCheckpoint(&ms);

    char *ptr = myLeak.NewChar();
    _CrtMemDumpAllObjectsSince(&ms);

    OutputDebugString("Exiting main()\n");
    return EXIT_SUCCESS;
}

调试输出日志

Dumping objects ->
{69} normal block at 0x000000000048B800, 1024 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
Exiting main()
Detected memory leaks!
Dumping objects ->
{69} normal block at 0x000000000048B800, 1024 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
{68} normal block at 0x000000000048B390, 1024 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

请注意,第一个转储只记录内部分配,因为我使用了一个检查点,然后是一个 dump-since 调用。第二个转储(在main()完成后调用)仍然记录它们,因为它们都很出色。

您在动态分配后泄漏转储(授予来自全局内存空间中的对象,但这与分配无关,它只是分配内存并将其返回给您的代码)看起来不正确。自程序启动以来,您应该通过任何调用获得所有未完成的 CRT 分配的对象转储。_CrtDumpMemoryLeaks()

于 2013-01-28T05:46:11.447 回答
0

想想看。全局变量myClass具有静态存储持续时间。它会main在程序终止时的右大括号之后被销毁。_CrtDumpMemoryLeaks() 不能肯定地检测为泄漏,因为MyClass尚未(正确)调用的析构函数。

于 2013-01-28T05:44:32.123 回答
0

自从我的程序退出后,我不应该收到警告吗(就在_CrtDumpMemoryLeaks()之后)

是的,您应该收到警告,但不是因为退出,...

还有一个新的字符没有被删除?

...但正因为如此,即因为仍然有一个动态分配的char 数组没有被删除。

如何检测这些内存泄漏?

你确定你用 _DEBUG #defined 编译了程序吗?如果你这样做了,afaik 它应该被检测到。(现在无法检查自己并且已经很久没有使用它了,所以我不能保证这一点。)

另外,使用上面的相同示例,如果我添加代码:

char* anotherPointer = charPointer; //previously filled 删除[]另一个指针;`

这会从类内部删除新的字符以防止内存泄漏,还是我也应该在 charPointer 上调用 delete?

这将删除动态分配的字符数组。在这之后charPointer是一个悬空指针,它不能再被使用——既不被取消引用,也不被删除[]。(同样适用于anotherPointer。)再次删除已删除的数组,无论是通过相同的指针还是另一个变量都是不允许的,并且会导致Undefined Behavior

于 2013-01-28T05:52:05.330 回答
0

您是否与调试库链接?如果不是,您将不会收到有关未释放内存块的消息。提示:使用 /MDd 选项。

请注意,与函数名称所暗示的相反,它所做的只是打印尚未释放的内存块列表。通过在 main() 的末尾调用它,您可以让它报告您明确分配但没有明确释放的块。因此,如果您有动态分配的单例对象,它们将被报告为泄漏,而您可能完全合法地使用通过 _atexit() 机制连接的函数来释放它们。

于 2013-01-28T05:53:56.577 回答