2012-03-19 70 views
1

我维持旧的C++应用程序,其具有如下面很多类:紧凑矢量小的存储器脚印矢量

class ClassWithALotOfVectors 
    { 
     std::vector<int> _vector1; 
     std::vector<int> _vector2; 

     // a lot of other vector datamembers go here 

     std::vector<double> _vectorN; 
    }; 

那就是 - 一个数据类型,其中成员的载体双或者int。 的关键是 - 这些载体是从来没有填充在同一时间 - 因此 当我们创建ClassWithALotOfVectors 100000个实例 - 内存使用量增加了 令人印象深刻的数字,即使只有10%,这些载体都在使用。

所以我决定写一个小的“按需分配”矢量类。

它周围的std ::向量的包装 - 其中内部向量仅创建时 首次访问(使用两个吸气剂 - REF()& const_ref()方法)

当我取代的std ::向量在ClassWithALotOfVectors类,如下一个compact_vector :

class ClassWithALotOfVectors2 
    { 
    compact_vector<int> _vector1; 
    compact_vector<int> _vector2; 

    compact_vector<double> _vectorN; 
    }; 

做了一些测试,结果是有希望的 - 内存使用量下降 显着,却突然发现,应用程序不释放内存 在最后 - 内存消耗增长的速度远远低于它以前的 - 但应用程序似乎并没有在最后解除分配内存 。

你可以看一下我的执行compact_vector ,看看你是否能发现什么毛病内存管理。

template <class T> class compact_vector 
    { 
    public: 
      compact_vector<T>() 
      :_data(NULL) 
      { 
      } 

      ~compact_vector<T>() 
      { 
        clear(); 
      } 

      compact_vector<T>(const compact_vector<T> & rhs) 
      :_data(NULL) 
      { 
        if (NULL != rhs._data) 
        { 
          _data = new std::vector<T>(*(rhs._data)); 
        } 
        else 
        { 
          clear(); 
        } 
      } 

      //  assignment 
      // 
      compact_vector<T> & operator=(const compact_vector<T> & rhs) 
      { 
        if (this != &rhs) 
        { 
          clear(); 
          if (NULL != rhs._data) 
          { 
            _data = new std::vector<T>(*rhs._data); 
          } 
        } 
        return *this; 
      } 
      compact_vector<T> & operator=(const std::vector<T> & rhs) 
      { 
        clear(); 
        if (!rhs.empty()) 
        { 
          _data = new std::vector<T>(rhs); 
        } 
        return *this; 
      } 

      const std::vector<T> & const_ref() 
      { 
        createInternal(); 
        return *_data; 
      } 
      std::vector<T> & ref() 
      { 
        createInternal(); 
        return *_data; 
      } 
      void clear() 
      { 
        if (NULL != _data) 
        { 
          _data->clear(); 
          delete _data; 
          _data = NULL; 
        } 
      } 
    private: 
      void createInternal() 
      { 
        if (NULL == _data) 
        { 
          _data = new std::vector<T>(); 
        } 
      } 

    private: 
      compact_vector<T>(const std::vector<T> & rhs) 
      { 
      } 
      compact_vector<T>(std::vector<T> & rhs) 
      { 
      } 
      compact_vector<T>(std::vector<T> rhs) 
      { 
      } 

      std::vector<T> * _data; 
    }; 
+3

你'clear'方法可以大大简化为'删除_data; _data = 0;' – 2012-03-19 11:16:34

+1

如果可能,你应该使用'nullptr',尽管'NULL'仍然可以。 – leftaroundabout 2012-03-19 11:19:49

+1

[stack exchange](http://stackexchange.com/)有一个[code review](http://codereview.stackexchange.com/)部分 – 2012-03-19 11:28:03

回答

3

std::vector的大多数实现在获取内容之前不会获取内存,而且矢量的大小通常只是少数(3 +可能是额外的调试信息)指针。也就是说,std::vector<int>()不会为任何对象分配空间。我相信你在这里吠叫错了树。

为什么您的原始代码有更高的内存使用量?你期望clear()释放内存?

如果是这样,你是错了。 clear()函数销毁包含的元素,但不释放分配的内存。考虑加入一个包装以清除使用下列成语内存:

std::vector<int>().swap(_data); 

什么前行确实是创建一个新载体(一般不内存连接,而不是强制的,但常见的实现),并交换了这两个向量的内容。在表达式结尾处,临时数据包含最初由_data向量保存的数据,而_data为空且没有内存(已定义实现)。在完整表达式结束时,临时文件被破坏,并释放内存。

或者,如果您的编译器支持C++ 11,则在致电clear()后可以使用shrink_to_fit()。该标准不需要shrink_to_fit()实际上缩小到适合,但对于一个空向量,我希望实现这样做。在呼叫之后通过呼叫capacity()进行测试,并查看它是否降到0。

+0

即使clear()不会释放内存,下一行delete会这样做,不是吗? – Andrew 2012-03-19 12:30:43

+0

@Andrew我正在谈论原始的一段代码,而不是'compact_vector'。虽然你可以修复'compact_vector',这只是增加了我认为不必要的复杂性。是的,矢量的销毁会释放内存,就像临时交换一样,但是后者将从应用程序中删除大量代码,从而减少错误(包括当前内存泄漏)的可能性。 – 2012-03-19 12:32:58

+1

@Andrew:如果不明确:我建议通过删除整个类型的需要来消除'compact_vector'中的错误。如果你的内存占用很高,只是由于* empty *矢量,那么你可能想要真正考虑设计。 – 2012-03-19 12:40:29

1

Use smart pointersIn this case, std::unique_ptr与手工制作复制ctor /赋值运算符。突然,你所有的问题都解决了。这就像魔术一样!

对不起,声音如此犀利,但内存泄漏总是有相同的答案:智能指针。

+0

难道这不会增加内存消耗大约是什么原始矢量? – 2012-03-19 11:47:47

+0

@BoPersson'unique_ptr'的任何理智的实现与指针具有相同的占用空间。 – 2012-03-19 12:07:09

+1

@Xeo不幸的是我不能使用std :: unique_ptr,因为它只能在最新版本的C++中使用,而我的应用程序必须在Visual Studio 2005上编译,而Visual Studio 2005与C++ 0x兼容5年。更多的 - 我真的不知道我的compact_vector在哪里泄漏内存,如果有的话...... – Andrew 2012-03-19 12:18:37

0

既然你说这是一个旧的应用程序,不能用它做很多。如何在施工期间保留所有具有零尺寸的矢量。
这是乏味的,但由于旧的应用程序,并没有太多预计将被修改。可能值得一次的努力。

class ClassWithALotOfVectors  
{  
    std::vector<int> _vector1;  
    std::vector<int> _vector2;   
    // a lot of other vector datamembers go here   
    std::vector<double> _vectorN; 

    public: 
    ClassWithALotOfVectors() : Init() { } 
    void Init() 
    { 
     vector1.reserve(0); 
     /// Like wise other vectors. 
    } 
}; 
+0

谢谢,但不是'reserve(0)'是默认的'vector'构造函数默认做了什么? – Andrew 2012-03-23 23:42:26

0

一次只有一个矢量使用的想法听起来像union概念。果然,C++有一个名为anonymous union的元素,就是这样做的。

class ClassWithALotOfVectors { 
    public: 
    union { 
     std::vector<double> _vector1; 
     std::vector<double> _vector2; 
    }; 
    ClassWithALotOfVectors() { new(&_vector1) std::vector<double>; }; 
    ~ClassWithALotOfVectors() { _vector1.~vector<double>(); }; 
}; 

因为工会无法知道调用哪个元素的构造函数,默认的构造函数在工会的残疾人,你必须手动构建和解构工会的元素,但我认为这将实现你在找什么,将_vector1_vector2混淆到相同的元素,但将两者都放置在ClassWithALotOfVectors名称空间中,然后在调用析构函数ClassWithALotOfVectors时解除分配矢量。

更多关于匿名联合看到: CPP reference: Anonymous Unions