解决由全局静态变量引起的内存问题

时间:2012-10-22 12:27:43

标签: c++ memory-management static global-variables valgrind

警告:这个问题的出现是因为我必须处理大量糟糕的代码,而没有适当的文档,6年前由其他人编写为研究项目。显然,一个更好的解决方案是首先导致这些问题,并采用适当的设计......

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

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

    file“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函数下面的exit处理程序中被调用时,该变量被释放,但是static_loader下面的main_initialization_and_destruction_0函数中的也是

    有没有办法确保这些变量一旦完全被释放,而不涉及广泛地重新分解代码?在我必须使用的库中,有几十个这种模式的实例......

    编辑:

    添加功能:

        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;
        }
    

    解决所有问题。我的库有内存泄漏,无论如何都有这样的来源,但我不再确定如何重现它。至少它不是这个,并且感谢关于重构等的建议!

3 个答案:

答案 0 :(得分:2)

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

当班级违反三级规则时,任务会导致问题就不足为奇了。

答案 1 :(得分:1)

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

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

稍后,在cause_static_global_creation中使用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.不要在堆上构造向量,而是使用实例成员。

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

答案 2 :(得分:1)

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

  1. 如果您拥有class.h,则只需将vector<int>*替换为vector即可。编译器将负责堆栈内存管理,您将避免堆泄漏。
  2. 请注意,全局静态将在程序执行期间消耗堆栈空间,因此逐字节逐字地表示为“动态内存泄漏”。
  3. 您的双重删除可能是由对指针转义的引用引起的,正如Benji所指出的那样,默认复制ctor很可能。您可以用共享指针(不需要删除调用)替换它,但堆栈更好。考虑禁用复制构造函数或编写深拷贝而不是浅拷贝的拷贝文件,并且如果您禁用分配和复制构造并且它没有编译您发现(一个)您的问题,Benji会进一步注意。 / LI>
  4. 全局静态不应该是内存使用中的问题,除非它们是一个集合并且无限期地继续扩展而不会在使用后删除它们的垃圾。如果内部的向量在没有被清理的情况下无法无限扩展,那么这些向量在程序执行方面消耗了恒定的内存量。确保这些使用寿命更短可能会产生更好的代码,但如果您关注的是内存,则会被认为是过早优化。