紧凑的矢量小内存足迹矢量

时间:2012-03-19 11:03:19

标签: c++ memory-management stl vector

我维护一个旧的C ++应用程序,它有很多类,如下所示:

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

        // a lot of other vector datamembers go here

        std::vector<double> _vectorN;
    };

即 - 一个数据类型,其中成员是double或int的向量。 问题是 - 这些载体永远不会同时填充 - 因此 当我们创建100000个ClassWithALotOfVectors实例时 - 内存使用量会增加 令人印象深刻的数字,即使这些载体中只有10%正在使用中。

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

它是std :: vector的包装器 - 其中内部向量仅在何时创建 第一次访问(使用两个getter - ref()&amp; const_ref()方法)

当我用Compact_vector替换ClassWithALotOfVectors类中的std :: 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; 
    }; 

4 个答案:

答案 0 :(得分:3)

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

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

如果是这样你就错了。 clear()函数会破坏包含的元素,但不会释放已分配的内存。考虑添加一个包装器来清除使用以下习语的内存:

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

前一行的作用是创建一个新的向量(通常没有附加内存,没有强制要求,但是常见的实现)并交换两个向量的内容。在表达式的末尾,临时包含最初由_data向量保持的数据,_data为空且没有内存(实现已定义)。在完整表达式结束时会破坏临时值并释放内存。

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

答案 1 :(得分:1)

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

很抱歉听起来如此狡猾,但内存泄漏总是有相同的答案:智能指针。

答案 2 :(得分: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.   
     }  
};

答案 3 :(得分: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>(); };
};

因为union无法知道要调用哪个元素的构造函数,所以在union中禁用了默认构造函数,你必须手动构造和解构union的元素,但我认为这样做完成你要找的东西,将_vector1_vector2别名化为同一个元素但将两者放在ClassWithALotOfVectors命名空间中,然后在调用ClassWithALotOfVectors的析构函数时释放向量

有关匿名工会的更多信息,请参阅: CPP reference: Anonymous Unions