【问题标题】:Implementation of Vector in C++ [closed]C++中向量的实现[关闭]
【发布时间】:2011-03-01 19:01:29
【问题描述】:

我最近写了一个 STL Vector 的实现作为编程练习。该程序编译但我收到一个奇怪的错误说:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc

我以前从未遇到过这个错误,也不确定在我的实现中究竟应该改变什么以使其正常运行。

有人可以看一下我的代码,看看在这种特定情况下是否有任何错误之处?抱歉,我不能更具体,我不确定自己在哪里看,提前谢谢。

#include <iostream>
#include <string>
#include <cassert>
#include <algorithm>

using namespace std;

template <class T>
class Vector
{
public:

   typedef T * iterator;

   Vector();
   Vector(unsigned int size);
   Vector(unsigned int size, const T & initial);
   Vector(const Vector<T> & v);      
   ~Vector();

   unsigned int capacity() const;
   unsigned int size() const;
   bool empty() const;
   iterator begin();
   iterator end();
   T & front();
   T & back();
   void push_back(const T & value); 
   void pop_back();  

   void reserve(unsigned int capacity);   
   void resize(unsigned int size);   

   T & operator[](unsigned int index);  
   Vector<T> & operator=(const Vector<T> &);

private:
   unsigned int my_size;
   unsigned int my_capacity;
   T * buffer;
};

// Your code goes here ...
template<class T>
Vector<T>::Vector()
{
    my_capacity = 0;
    my_size = 0;
    buffer = 0;
}

template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
    my_size = v.my_size;
    my_capacity = v.my_capacity;
    buffer = new T[my_size];  
    for (int i = 0; i < my_size; i++)
        buffer[i] = v.buffer[i];  
}

template<class T>
Vector<T>::Vector(unsigned int size)
{
    my_capacity = size;
    my_size = size;
    buffer = new T[size];
}

template<class T>
Vector<T>::Vector(unsigned int size, const T & initial)
{
    my_size-size;
    my_capacity = size;
    buffer = new T [size];
    for (int i = 0; i < size; i++)
        buffer[i] = initial;
        T();
}

template<class T>
Vector<T> & Vector<T>::operator = (const Vector<T> & v)
{
    delete[ ] buffer;
    my_size = v.my_size;
    my_capacity = v.my_capacity;
    buffer = new T [my_size];
    for (int i = 0; i < my_size; i++)
        buffer[i] = v.buffer[i];
    return *this;
}

template<class T>
typename Vector<T>::iterator Vector<T>::begin()
{
    return buffer;
}

template<class T>
typename Vector<T>::iterator Vector<T>::end()
{
    return buffer + size();
}

template<class T>
T& Vector<T>::Vector<T>::front()
{
    return buffer[0];
}

template<class T>
T& Vector<T>::Vector<T>::back()
{
    return buffer[size - 1];
}

template<class T>
void Vector<T>::push_back(const T & v)
{
    if (my_size >= my_capacity)
    reserve(my_capacity +5);
    buffer [my_size++] = v;
}

template<class T>
void Vector<T>::pop_back()
{
    my_size--;
}

template<class T>
void Vector<T>::reserve(unsigned int capacity)
{
    if(buffer == 0)
    {
        my_size = 0;
        my_capacity = 0;
    }    
    T * buffer = new T [capacity];
    assert(buffer);
    copy (buffer, buffer + my_size, buffer);
    my_capacity = capacity;
    delete[] buffer;
    buffer = buffer;

}

template<class T>
unsigned int Vector<T>::size()const//
{
    return my_size;
}

template<class T>
void Vector<T>::resize(unsigned int size)
{
    reserve(size);
    size = size;
}

template<class T>
T& Vector<T>::operator[](unsigned int index)
{
    return buffer[index];
}  

template<class T>
unsigned int Vector<T>::capacity()const
{
    return my_capacity;
}

template<class T>
Vector<T>::~Vector()
{
    delete[]buffer;
}


int main()
{  

   Vector<int> v;

   v.reserve(2);
   assert(v.capacity() == 2);

   Vector<string> v1(2);
   assert(v1.capacity() == 2);
   assert(v1.size() == 2);
   assert(v1[0] == "");
   assert(v1[1] == "");

   v1[0] = "hi";
   assert(v1[0] == "hi");

   Vector<int> v2(2, 7);
   assert(v2[1] == 7);

   Vector<int> v10(v2);
   assert(v10[1] == 7);

   Vector<string> v3(2, "hello");
   assert(v3.size() == 2);
   assert(v3.capacity() == 2);
   assert(v3[0] == "hello");
   assert(v3[1] == "hello");

   v3.resize(1);
   assert(v3.size() == 1);
   assert(v3[0] == "hello");

   Vector<string> v4 = v3;
   assert(v4.size() == 1);
   assert(v4[0] == v3[0]);
   v3[0] = "test";
   assert(v4[0] != v3[0]);  
   assert(v4[0] == "hello");

   v3.pop_back();
   assert(v3.size() == 0);

   Vector<int> v5(7, 9);
   Vector<int>::iterator it = v5.begin();
   while (it != v5.end())
   {
      assert(*it == 9);
      ++it;
   }

   Vector<int> v6;
   v6.push_back(100);
   assert(v6.size() == 1);
   assert(v6[0] == 100);
   v6.push_back(101);
   assert(v6.size() == 2);
   assert(v6[0] == 100);
   v6.push_back(101);

   cout << "SUCCESS\n";
}

【问题讨论】:

  • 您应该使用调试器运行您的程序,以便查看引发异常的位置。
  • 你能缩小 main() 中出现错误的那一行吗?
  • 我建议您至少为每种方法创建一个单元测试。
  • Vector&lt;T&gt;::Vector(unsigned int size, const T &amp; initial) T() 你那里有什么?

标签: c++ exception vector stl template-meta-programming


【解决方案1】:

这是完整的源代码,从您的源代码更新:

    #pragma once


//using namespace std;

template <class T>
class  Vector
{
public:

    typedef T * iterator;

    Vector();
    Vector(unsigned int size);
    Vector(unsigned int size, const T & initial);
    Vector(const Vector<T> & v);      
    ~Vector();

    unsigned int capacity() const;
    unsigned int size() const;
    bool empty() const;
    iterator begin();
    iterator end();
    T & front();
    T & back();
    void push_back(const T & value); 
    void pop_back();  

    void reserve(unsigned int capacity);   
    void resize(unsigned int size);   

    T & operator[](unsigned int index);  
    Vector<T> & operator=(const Vector<T> &);
    void clear();
private:
    unsigned int my_size;
    unsigned int my_capacity;
    T * buffer;
};

// Your code goes here ...
template<class T>
Vector<T>::Vector()
{
    my_capacity = 0;
    my_size = 0;
    buffer = 0;
}

template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
    my_size = v.my_size;
    my_capacity = v.my_capacity;
    buffer = new T[my_size];  
    for (unsigned int i = 0; i < my_size; i++)
        buffer[i] = v.buffer[i];  
}

template<class T>
Vector<T>::Vector(unsigned int size)
{
    my_capacity = size;
    my_size = size;
    buffer = new T[size];
}

template<class T>
Vector<T>::Vector(unsigned int size, const T & initial)
{
    my_size = size;
    my_capacity = size;
    buffer = new T [size];
    for (unsigned int i = 0; i < size; i++)
        buffer[i] = initial;
    //T();
}

template<class T>
Vector<T> & Vector<T>::operator = (const Vector<T> & v)
{
    delete[ ] buffer;
    my_size = v.my_size;
    my_capacity = v.my_capacity;
    buffer = new T [my_size];
    for (unsigned int i = 0; i < my_size; i++)
        buffer[i] = v.buffer[i];
    return *this;
}

template<class T>
typename Vector<T>::iterator Vector<T>::begin()
{
    return buffer;
}

template<class T>
typename Vector<T>::iterator Vector<T>::end()
{
    return buffer + size();
}

template<class T>
T& Vector<T>::front()
{
    return buffer[0];
}

template<class T>
T& Vector<T>::back()
{
    return buffer[my_size - 1];
}

template<class T>
void Vector<T>::push_back(const T & v)
{
    if (my_size >= my_capacity)
        reserve(my_capacity +5);
    buffer [my_size++] = v;
}

template<class T>
void Vector<T>::pop_back()
{
    my_size--;
}

template<class T>
void Vector<T>::reserve(unsigned int capacity)
{
    if(buffer == 0)
    {
        my_size = 0;
        my_capacity = 0;
    }    
    T * Newbuffer = new T [capacity];
    //assert(Newbuffer);
    unsigned int l_Size = capacity < my_size ? capacity : my_size;
    //copy (buffer, buffer + l_Size, Newbuffer);

    for (unsigned int i = 0; i < l_Size; i++)
        Newbuffer[i] = buffer[i];

    my_capacity = capacity;
    delete[] buffer;
    buffer = Newbuffer;
}

template<class T>
unsigned int Vector<T>::size()const//
{
    return my_size;
}

template<class T>
void Vector<T>::resize(unsigned int size)
{
    reserve(size);
    my_size = size;
}

template<class T>
T& Vector<T>::operator[](unsigned int index)
{
    return buffer[index];
}  

template<class T>
unsigned int Vector<T>::capacity()const
{
    return my_capacity;
}

template<class T>
Vector<T>::~Vector()
{
    delete[ ] buffer;
}
template <class T>
void Vector<T>::clear()
{
    my_capacity = 0;
    my_size = 0;
    buffer = 0;
}

【讨论】:

  • return buffer[size-1];T&amp; Vector&lt;T&gt;::back() 方法中应该是return buffer[my_size-1];
  • pop_back 应该确保释放最后一个元素所拥有的任何资源,否则你注定会发生内存泄漏。让它buffer[my_size]-&gt;~T(); --my_size;
【解决方案2】:

也许是这个错字?

Vector<T>::Vector(unsigned int size, const T & initial)
{
    my_size-size; 

【讨论】:

  • @Mark Ransom,不,这仍然给我留下了同样的错误。不过谢谢,那是错误的。
  • @Meursault:在resize 中还有一个size = size;,应该是my_size = size;。而buffer = buffer; 中的reserve 应该是this-&gt;buffer = buffer;
【解决方案3】:

你的“储备金”坏了。为本地缓冲区使用另一个变量名。

【讨论】:

  • 更清楚地说,您永远不会设置 this.buffer,因为您的局部变量名为 buffer。因此,您实际上从未预留空间,但每次调用预留时都会泄漏内存。
【解决方案4】:

除了需要修复您的reserve 函数之外,您的复制构造函数和复制分配操作符还有一个有趣的问题:

Vector<T> t1 = t2;

这将设置 t1 的容量等于 t2 的容量(变量),但 t1 的实际容量将是 t2 的大小;因此,当您在复制构造函数/赋值运算符之后开始将元素推送到向量上时,您将遇到缓冲区溢出问题。

你需要把它改成

template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
    my_size = v.my_size;
    my_capacity = v.my_capacity;
    buffer = new T[my_capacity];
    memcpy(buffer, v.buffer, my_size * sizeof(T));  
}

或者(如果你想让它调整为更小的数组)

template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
    my_size = v.my_size;
    my_capacity = v.my_size;
    buffer = new T[my_size];
    memcpy(buffer, v.buffer, my_size * sizeof(T));  
}

【讨论】:

    【解决方案5】:

    这段代码没有为我编译。 Clang 抱怨第 114 行(back() 的实现)期望调用“size”。

    我认为该行是“return buffer[size() -1];”

    它还给出了有关此构造函数实现的警告: 模板 Vector::Vector(unsigned int size, const T & initial)

    第一行可能应该是“my_size = size;” (此构造函数的)最后一行可能应该被删除。

    接下来,它在第 209 行的断言失败:assert(v3.size() == 1);

    这会打开一大堆蠕虫,但明显的问题在于 resize() 中的一行:“size = size;”这可能意味着“my_size = size;”

    有了这个改变,我们现在在第 121 行崩溃,它在从第 231 行调用的 push_back() 中 "v6.push_back(100);"

    由于reserve() 中的问题而失败。我们正在创建一个与成员变量同名的局部变量“缓冲区”。让我们将名称更改为 temp_buffer。注意:不要 assert() 运行时错误。 assert() 用于逻辑错误。这个 assert() 不能失败。 new 永远不会返回 0。它会改为抛出。

    在reserve() 中进行了明显的修复之后(还有其他问题),我们现在在resize() 调用resize() 中的reserve() 中崩溃了 208 in main(), "v3 .resize(1);".

    当我们减少容量时,我们看到储备实际上是在分配一个新的缓冲区。这既是性能损失,也是可靠性损失(内存分配可能失败)。但是我们仍然不应该崩溃,所以我们会在不解决明显的设计缺陷的情况下尝试防止崩溃。

    崩溃即将来临,因为我们正在将容器中存在的所有项目复制到新分配的数组中。如果我们只在需要增加容量时才这样做,这将是正确的,但在这种情况下,我们的物品数量超出了新容量所能容纳的数量。如果 my_size 大于该值,则代码应将其设置为新容量。

    现在测试代码报告“SUCCESS”。

    但是这段代码仍然有很多问题。

    最大的问题之一是我们没有在分配的数组中使用未初始化的内存。这样做是 std::vector 标准所要求的,它同时具有性能和可靠性优势。但它也使代码复杂化,因此这可能是我们可以接受的一种捷径,显然是一种智力练习。

    构造函数:使用初始化语法来初始化数据成员。

    使用您的复制构造函数和初始值的构造函数,如果您的任何循环赋值抛出异常,您将泄漏分配的数组。

    赋值运算符应该分配一个大小为“my_capacity”而不是“my_size”的新缓冲区,尽管有一个明显的优化,如果右侧对象的大小不大于“this”对象,我们应该根本不分配。

    如果在赋值运算符中分配新数组失败,我们已经删除了缓冲区,所以我们最终(当我们的 Vector 对象被销毁时)会双重删除缓冲区,我们之前可能会彻底崩溃那么。

    在 push_back() 中,为了支持标准的性能保证,我们需要将容量增加现有容量大小的某个恒定比例。例如:“reserve(my_capacity * 1.5);”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-26
      • 1970-01-01
      • 2015-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多