最小的C ++ STL向量实现问题

时间:2011-10-13 11:20:43

标签: c++ stl vector g++ segmentation-fault

我遇到了技术问题而且让我很困惑。我提前道歉,因为我可能没有提供相关细节;我还不知道为什么会出错,包含我正在使用的所有代码都会过分。

我正在使用一个使用C ++ STL的大型程序。我正在将此代码移动到一个非常敏感的环境中,没有标准clib或STL实现;它将重新定义malloc / free / new / delete等...为此,我需要用我自己的简化实现替换std :: parts。我开始使用std :: vector。现在它正在标准生态系统中运行,因此它是GNU libc和STL。唯一改变的是这个矢量类。

当我用替换的类执行程序时,会出现段错误。我通过GDB发现这一点,发现程序将使用下标运算符从向量请求一个对象。返回对象引用时,将调用方法并且程序会段错误。它似乎找不到这个方法,最终在GDB中的main_arena()中。对象的类型是继承的类。

我真的不确定这里的问题是什么。我很乐意提供更多细节,但我不确定我还能提供什么。我只能假设我的矢量实现有问题,因为程序中的其他内容都没有改变。也许有一些显而易见的事情,我在这里做错了,我根本没有看到。

我正在使用:g ++(GCC)4.4.5 20110214(Red Hat 4.4.5-6)

我真的很感激任何意见/建议!

#ifndef _MYSTL_VECTOR_H_
#define _MYSTL_VECTOR_H_

#include <stdlib.h>
#include <assert.h>

typedef unsigned int uint;

namespace mystl
{
    /******************
      VECTOR
    ********************/


    template <typename T>
    class vector 
    {

        private:

            uint _size;
            uint _reserved;
            T *storage;

            void init_vector(uint reserve)
            {
                if (reserve == 0)
                {
                    _reserved = 0;
                    return;
                }

                storage = (T*)malloc(sizeof(T)*reserve);
                assert(storage);

                _reserved = reserve;
            }

        public:
            vector()
            {
                    // std::cerr << "default constructor " << this << std::endl;
                    storage = NULL;
                    _size = 0;
                    _reserved = 0;
            }

            vector(const vector<T> &other)
            {
                // std::cerr << "copy constructor " << this << std::endl;

                storage = NULL;
                _size = 0;
                _reserved = 0;


                init_vector(other.size());
                _size = other.size();

                for (uint i=0; i<other.size(); i++)
                {
                    storage[i] = T(other[i]);
                }
            }

            vector(uint init_num, const T& init_value)
            {
                    // std::cerr << "special constructor1 " << this << std::endl;

                        storage = NULL;
                        _size = 0;
                        _reserved = 0;

                      init_vector(init_num);

                      for (size_t i=0; i<init_num; i++)
                      {
                          push_back(init_value);
                      }
            }

            vector(uint init_num)
            {
                    // std::cerr << "special constructor2 " << this << std::endl;

                        storage = NULL;
                        _size = 0;
                        _reserved = 0;

                      init_vector(init_num);
            }

            void reserve(uint new_size) 
            {   
                if (new_size > _reserved) 
                {

                    storage = (T*)realloc(storage, sizeof(T)*new_size);
                    assert(storage);

                    _reserved = new_size;
                }
            }

            void push_back(const T &item) 
            {
                if (_size >= _reserved) 
                {
                    if (_reserved == 0) _reserved=1;
                    reserve(_reserved*2);
                }

                storage[_size] = T(item);
                _size++;
            }

            uint size() const
            {
                return _size;
            }

            ~vector()
            {
                if (_reserved)
                {
                    free(storage);
                    storage = NULL;
                    _reserved = 0;
                    _size = 0;
                }
            }

            // this is for read only
            const T& operator[] (unsigned i) const
            {
                // do bounds check...
                if (i >= _size || i < 0)
                {
                    assert(false);
                }
                return storage[i];
            }


            T& operator[] (unsigned i)
            {
                // do bounds check...
                if (i >= _size || i < 0)
                {
                    assert(false);
                }
                return storage[i];
            }

            // overload = operator
            const vector<T>& operator= (const vector<T>& x)
            {
                // check for self
                if (this != &x)
                {   
                    _reserved = 0;
                    _size = 0;
                    storage = NULL;

                    init_vector( x.size() );

                    for(uint i=0; i<x.size(); i++)
                    {
                        storage[i] = T(x[i]);
                    }

                    _size = x.size();
                }

                return *this;
            }

            uint begin() const
            {
                return 0;
            }

            void insert(uint pos, const T& value)
            {
                push_back(value);
                if (size() == 1)
                {
                          return;
                }
                for (size_t i=size()-2; i>=pos&& i>=0 ; i--)
                {
                    storage[i+1] = storage[i];
                }
                storage[pos] = value;
            }

            void erase(uint erase_index)
            {
                if (erase_index >= _size) 
                {
                    return;
                }
                //scoot everyone down by one
                for (uint i=erase_index; i<_size; i++)
                {
                    storage[i] = storage[i+1];
                }
                _size--;
            }


            void erase(uint start, uint end)
            {

                if (start > end)
                {
                    assert(false);
                }

                if (end > _size)
                    end = _size;

                for (uint i=start; i<end; i++)
                {
                    erase(start);
                }

                assert(false);
            }

            void clear()
            {
                erase(0,_size);
            }

        bool empty() const
        {
            return _size == 0;
        }

    }; //class vector
}


#endif // _MYSTL_VECTOR_H_

3 个答案:

答案 0 :(得分:4)

哇!

  1. 您的赋值运算符也会泄漏内存。

  2. 因为你正在使用malloc / release,你的类型T的构造函数将不会被调用,因此除了最简单的对象之外你不能使用你的向量。

  3. 编辑:

    今天早上我有点无聊:试试这个

    #include <stdlib.h> // For NULL
    #include <new>      // Because you need placement new
    
    // Because you are avoiding std::
    // An implementation of swap
    template<typename T>
    void swap(T& lhs,T& rhs)
    {
        T   tmp = lhs;
        lhs = rhs;
        rhs = tmp;
    }
    
    
    template <typename T>
    class vector
    {
        private:
            unsigned int dataSize;
            unsigned int reserved;
            T*           data;
    
        public:
            ~vector()
            {
                for(unsigned int loop = 0; loop < dataSize; ++loop)
                {
                    // Because we use placement new we must explicitly destroy all members.
                    data[loop].~T();
                }
                free(data);
            }
            vector()
                : dataSize(0)
                , reserved(10)
                , data(NULL)
            {
                reserve(reserved);
            }
    
            vector(const vector<T> &other)
                : dataSize(0)
                , reserved(other.dataSize)
                , data(NULL)
            {
                reserve(reserved);
                dataSize = reserved;
                for(unsigned int loop;loop < dataSize;++loop)
                {
                    // Because we are using malloc/free
                    // We need to use placement new to add items to the data
                    // This way they are constructed in place
                    new (&data[loop]) T(other.data[loop]);
                }
            }
    
            vector(unsigned int init_num)
                : dataSize(0)
                , reserved(init_num)
                , data(NULL)
            {
                reserve(reserved);
                dataSize = reserved;
                for(unsigned int loop;loop < dataSize;++loop)
                {
                    // See above
                    new (&data[loop]) T();
                }
            }
    
            const vector<T>& operator= (vector<T> x)
            {
                // use copy and swap idiom.
                // Note the pass by value to initiate the copy
                swap(dataSize, x.dataSize);
                swap(reserved, x.rserved);
                swap(data,     x.data);
    
                return *this;
            }
    
            void reserve(unsigned int new_size)
            {
                if (new_size < reserved)
                {    return;
                }
    
                T*  newData = (T*)malloc(sizeof(T) * new_size);
                if (!newData)
                {    throw int(2);
                }
    
                for(unsigned int loop = 0; loop < dataSize; ++loop)
                {
                    // Use placement new to copy the data
                    new (&newData[loop]) T(data[loop]);
                }
                swap(data, newData);
                reserved    = new_size;
    
                for(unsigned int loop = 0; loop < dataSize; ++loop)
                {
                    // Call the destructor on old data before freeing the container.
                    // Remember we just did a swap.
                    newData[loop].~T();
                }
                free(newData);
            }
    
            void push_back(const T &item)
            {
                if (dataSize == reserved)
                {
                    reserve(reserved * 2);
                }
                // Place the item in the container
                new (&data[dataSize++]) T(item);
            }
    
            unsigned int  size() const  {return dataSize;}
            bool          empty() const {return dataSize == 0;}
    
            // Operator[] should NOT check the value of i
            // Add a method called at() that does check i
            const T& operator[] (unsigned i) const      {return data[i];}
            T&       operator[] (unsigned i)            {return data[i];}
    
            void insert(unsigned int pos, const T& value)
            {
                if (pos >= dataSize)         { throw int(1);}
    
                if (dataSize == reserved)
                {
                        reserve(reserved * 2);
                }
                // Move the last item (which needs to be constructed correctly)
                if (dataSize != 0)
                {
                    new (&data[dataSize])  T(data[dataSize-1]);
                }
                for(unsigned int loop = dataSize - 1; loop > pos; --loop)
                {
                    data[loop]  = data[loop-1];
                }
                ++dataSize;
    
                // All items have been moved up.
                // Put value in its place
                data[pos]   = value;
            }
    
            void clear()                                        { erase(0, dataSize);}
            void erase(unsigned int erase_index)                { erase(erase_index,erase_index+1);}
            void erase(unsigned int start, unsigned int end)    /* end NOT inclusive so => [start, end) */
            {
                if (end > dataSize)
                {   end     = dataSize;
                }
                if (start > end)
                {   start   = end;
                }
                unsigned int dst    = start;
                unsigned int src    = end;
                for(;(src < dataSize) && (dst < end);++dst, ++src)
                {
                    // Move Elements down;
                    data[dst] = data[src];
                }
                unsigned int count = start - end;
                for(;count != 0; --count)
                {
                    // Remove old Elements
                    --dataSize;
                    // Remember we need to manually call the destructor
                    data[dataSize].~T();
                }
            }
            unsigned int begin() const  {return 0;}
    
    
    }; //class vector
    

答案 1 :(得分:2)

使用当前的内存处理,此向量仅适用于普通的旧数据类型。

要处理所有类型,必须确保对象

  • 实际上已创建(malloc不会这样做),
  • 被毁(免费不做),
  • 并且您无法使用realloc重新分配内存,因为如果复杂对象按字节方式复制到另一个位置,则无法保证它们保持有效。

答案 2 :(得分:1)

看起来答案可以在你的问题中找到:&#34;当返回对象引用时,调用方法并且程序段错误。它似乎找不到这种方法,最终在GDB的main_arena()中。对象的类型是继承的类。&#34;

你可能将基类实例T存储在向量中,但是为从T继承的类的实例制作push_back。在push_back {storage [_size] = T(item);}中你进行了转换(实际上是复制构造函数T: T(const T&amp;))项到T(这可能命名为&#39; type cut&#39;),然后引用T并使用T的虚拟表调用从T继承的类的方法,其中方法不是定义了/抽象。我对吗? 要使其正常工作,您应将T *放在向量或shared_ptr / unique_ptr中,具体取决于您应用于向量元素的所有权条款。 通常在向量中,您只能存储POD(普通旧数据)类型。