全球宣布时内存泄露?

memory leaked when globaly declared?

本文关键字:内存 布时      更新时间:2023-10-16

如果不是将指针声明为 main 的一部分,而是全局声明它们,这段代码是否仍然会泄漏? 我用Valgrind memcheck测试过,它没有

class Test1 {
public:
Test1() { std::cout << "Constructor of Test " << std::endl; }
~Test1() { std::cout << "Destructor of Test "  << std::endl; }
};
//Memory leaked or not when globally declared?
// Test1 *t1;
// Test1 *t2;
// Test1 *t;
int main()
{
//mem will leak if not deallocated later
Test1 *t1;
Test1 *t2;
Test1 *t;
try {
t1=new Test1[100];
t2=new Test1;
t =new Test1;
throw 10;
}
catch(int i)
{
std::cout << "Caught " << i << std::endl;
// delete []t1;
// delete t;
// delete t2;
}
return 0;
}

声明变量全局将使指针变量全局,而不是指针指向的内容(它已经是全局的,因为它位于堆上(。

因此,您当前的实现也存在泄漏。

局部变量在超出范围时会被销毁,但它们指向的内容不会自动输出。建议:忘记完整性newdelete运算符,使用 STL 或智能指针。

编辑:你在问为什么valgrind没有检测到它,这是一个与原始问题不同的问题(我编辑以添加标签(。

现在你总是在泄漏内存,无论你是在 main 还是全局中声明指针。

每当在代码中使用new时,都需要使用deletedelete[]

在现代C++中,使用new被认为是一种不好的做法,如果你想要一个数组,你应该使用std::vector,或者如果你正在管理指向对象的指针,你应该使用std::unique_ptr

正如在其他答案中已经提到的,分配对象的析构函数不会在程序的两个变体中调用,指针的范围和生存期不会影响点头会发生什么,但您已经通过在析构函数中打印来证明这一点。

然而,瓦尔格林德的报告略有不同。

我使用较短的 2 个元素数组运行以减少输出量。

堆摘要告诉您运行结束时堆上保留哪些数据,对于两个程序是相同的:

==397== HEAP SUMMARY:
==397==     in use at exit: 12 bytes in 3 blocks
==397==   total heap usage: 6 allocs, 3 frees, 76,944 bytes allocated

这意味着两个程序都不会释放对象。

然而,Valgrind 确实在"绝对丢失"分配(不引用任何变量中剩余的内存块(和"仍可访问"分配(保留引用(之间产生了差异。

带有本地指针的泄漏摘要

==397== LEAK SUMMARY:
==397==    definitely lost: 12 bytes in 3 blocks
==397==    indirectly lost: 0 bytes in 0 blocks
==397==      possibly lost: 0 bytes in 0 blocks
==397==    still reachable: 0 bytes in 0 blocks
==397==         suppressed: 0 bytes in 0 blocks

带有全局指针的泄漏摘要

==385== LEAK SUMMARY:
==385==    definitely lost: 0 bytes in 0 blocks
==385==    indirectly lost: 0 bytes in 0 blocks
==385==      possibly lost: 0 bytes in 0 blocks
==385==    still reachable: 12 bytes in 3 blocks
==385==                       of which reachable via heuristic:
==385==                         length64           : 10 bytes in 1 blocks

如果指针是本地的,valgrind 可以确定没有引用保留,因为在主返回之后,堆栈位置不再有效。

如果指针是全局的,它们仍然有效,因此仍然可以使用或解除分配。

为什么瓦尔格林德要做出这种区分?

特别是在历史的 C 程序中,分配一些内存并在整个执行过程中使用它可能是合法的,而无需稍后释放内存。一旦程序退出,操作系统将清理程序的整个虚拟内存空间。因此,虽然这可能是一个错误,但它也可能是故意的。 如果您对此类泄漏感兴趣,valgrind 本身会告诉您必须如何调用才能看到它们:

==405== Reachable blocks (those to which a pointer was found) are not shown. 
==405== To see them, rerun with: --leak-check=full --show-leak-kinds=all                                                                                                                                

然而,"绝对丢失"的记忆总是可疑的,这就是瓦尔格林德区分这些案例的原因。像valgrind这样的工具的价值在于它的精度。仅仅报告许多实际错误是不够的,为了有用,还必须努力产生少量的误报,否则查看报告通常会浪费开发人员的时间。

在现代C++中,泄漏内存的借口并不多,因为std::unique_ptr应该是分配动态对象的方式。std::vector应该尽可能用于动态数组和本地对象,因为编译器永远不会忘记释放。即使对于单例,valgrind 和地址清理器等工具输出中的噪声通常也会超过保存一个析构函数调用或释放的通常微不足道的好处。