警告:出现这个问题是因为我必须处理一大堆糟糕的代码,没有适当的文档,由其他人在 6 年前作为研究项目编写。显然,更好的解决方案是首先不要通过适当的设计来引起这些问题......
也就是说,问题是:摆脱这种情况的最佳方法是什么:
- 类在堆上分配内存,并在析构函数中释放它。
- 在某处,类的实例在全局范围内声明。
- 存在初始化此实例的函数。
- 该函数的返回值用于初始化静态变量。
- 全局范围的变量在静态范围之外使用。
最小工作示例:
文件 "myclass.h":
#ifndef MYCLASS_H
#define MYCLASS_H
#include<vector>
using namespace std;
class myclass{
vector<int> *onTheHeap;
public:
myclass(int len=0){
onTheHeap = new vector<int>(len);
}
~myclass(){
delete onTheHeap;
}
};
#endif
文件"static_loader.cpp">
#include "class.h"
myclass existsForever;
int cause_static_global_creation(){
existsForever = myclass(5);
}
static int bootstrap = cause_static_global_creation();
并提交"main.cpp":
#include "class.h"
extern myclass existsForever;
int main(){
return 0;
}
构建方式:
g++ -g -c static_loader.cpp
g++ -g main.cpp static_loader.o
并运行为:
valgrind --leak-check=full ./a.out
结果:当变量的析构函数在 main 下方的出口处理程序中调用时,该变量被释放,但在 main下面的 static_initialization_and_destruction_0 函数中也被释放 static_loader!
有没有办法确保这些变量只释放一次,而不涉及大量重构代码?在我必须使用的库中,有几十个这种模式的实例......
编辑:
添加函数:
void operator=(myclass other){
delete this->onTheHeap;
this->onTheHeap = other.onTheHeap;
}
和
myclass(const myclass& other){
this->onTheHeap = new vector<int>(*(other.onTheHeap));
}
不会更改行为。
第二次编辑:
myclass& operator=(const myclass& other){
delete this->onTheHeap;
this->onTheHeap = new vector<int>(*(other.onTheHeap));
return *this;
}
解决所有问题。无论如何,我的库都有这样的来源的内存泄漏,但我不再确定如何重现它。至少不是这个,也感谢关于重构等的建议!
你的假设被打破了。myclass existsForever;
不是由cause_static_global_creation
初始化,而是由myclass::myclass
初始化。相反,cause_static_global_creation
为已初始化的对象赋值。
由于该类违反了三法则,因此作业导致问题也就不足为奇了。
我认为这些评论/策略涵盖了您的情况:
- 如果您拥有class.h,则只需将
vector<int>*
替换为vector
即可。 编译器将负责堆栈内存管理,您将避免堆泄漏。 - 请注意,全局静态将在程序执行期间消耗堆栈空间,因此逐字节这与动态内存泄漏一样"糟糕"。
- 您的双重删除可能是由对指针转义的引用引起的,正如 Benji 通过默认复制 ctor 指出的那样。 你可以用共享指针(不需要调用删除)替换它,但堆栈更好。 考虑禁用复制构造函数或编写一个深层复制而不是浅拷贝的复制 ctor,正如 Benji 进一步指出的那样,如果您禁用赋值和复制构造并且它没有编译,您已经发现了(其中一个)您的问题。
- 全局静态在内存使用中应该不是问题,除非它们是一个集合并且无限期地扩展,而不会在使用后删除它们的垃圾。 如果内部的向量没有被清理而无限扩展,那么这些向量在程序执行方面消耗恒定量的内存。 确保这些具有更紧密的生命周期将导致更好的因子代码,但如果您担心内存,则会被视为过早优化。
在您的示例中构造了两次myclass
。
首先,通过语句myclass existsForever;
len=0
。
稍后,使用len=5
在cause_static_global_creation
中构造一个临时实例,并使用默认赋值运算符将其分配给existsForever
。此时,existsForever
和临时指针将共享相同的onTheHeap
值。临时在cause_static_global_creation
中立即被销毁,释放了矢量的内存。当全局实例被销毁时,相同的内存会在程序端再次释放。
我有一些建议如何快速解决这个问题。
1.定义构造函数如下
myclass(int len=0)
{
if ( len > 0 )
onTheHeap = new vector<int>(len);
else
onTheHeap = NULL;
}
2.使用智能指针而不是裸指针。
std::shared_ptr<vector <int>> onTheHeap;
3.不要在堆上构造向量,而是使用实例成员。
更长的方法是正确实现赋值运算符和复制构造函数。