4

警告:之所以出现这个问题,是因为我必须处理一大堆糟糕的代码,没有适当的文档,这些代码是 6 年前由其他人作为研究项目编写的。显然,更好的解决方案是首先通过适当的设计不引起这些问题......

也就是说,问题是:摆脱这种情况的最佳方法是什么:

  1. 一个类在堆上分配内存,并在析构函数中释放它。
  2. 在某个地方,该类的一个实例在全局范围内声明。
  3. 存在一个初始化此实例的函数。
  4. 该函数的返回值用于初始化静态变量。
  5. 全局范围的变量在静态范围之外使用。

最小的工作示例:

文件“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;
    }

解决所有问题。无论如何,我的图书馆有这样的来源的内存泄漏,但我不再确定如何重现它。至少不是这样,也感谢重构等方面的建议!

4

3 回答 3

2

你的假设被打破了。myclass existsForever;不是由 初始化cause_static_global_creation,而是由初始化myclass::myclass。相反,cause_static_global_creation为已经初始化的对象分配一个值。

由于课程违反了三原则,因此作业引起问题也就不足为奇了。

于 2012-10-22T12:48:43.807 回答
1

myclass在您的示例中构造了两次。

首先,用len=0by 语句myclass existsForever;

cause_static_global_creation稍后,在with中构造了一个临时实例,len=5并使用默认赋值运算符将其分配给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.不要在堆上构造向量,而是使用实例成员。

更长的方法是正确实现赋值运算符和复制构造函数。

于 2012-10-22T12:46:14.210 回答
1

我认为这些评论/策略涵盖了您的情况:

  1. 如果您拥有 class.h 则只需将 替换vector<int>*vector. 编译器将负责堆栈内存管理,您将避免堆泄漏。
  2. 请注意,全局静态将在程序执行期间消耗堆栈空间,因此逐字节这与动态内存泄漏一样“糟糕”。
  3. 您的双重删除可能是由对指针转义的引用引起的,正如 Benji 通过默认复制 ctor 所指出的那样。您可以将其替换为共享指针(不需要调用 delete),但堆栈更好。考虑禁用复制构造函数或编写深复制而不是浅复制的复制 ctor,并且正如 Benji 进一步指出的那样,如果您禁用赋值和复制构造并且它不编译,您已经找到了(一个)您的问题。
  4. 全局静态在内存使用中不应该成为问题,除非它们是一个集合并且无限期地扩展而不会在使用后删除它们的垃圾。如果里面的向量在没有被清理的情况下不会无限扩展,那么这些向量在程序执行方面会消耗一定数量的内存。确保这些具有更短的生命周期将导致更好的分解代码,但如果您关心的是内存,则将被视为过早优化。
于 2012-10-22T12:46:50.093 回答