14

私は最近、プログラミングの練習として 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";
}
4

5 に答える 5

20

ソースから更新された完全なソースコードは次のとおりです。

    #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;
}
于 2012-10-24T07:32:27.667 に答える
1

関数を修正する必要があることに加えてreserve、copy-constructorとcopy-assignment-operatorには興味深い問題があります。

Vector<T> t1 = t2;

これにより、t1の容量はt2の容量(変数)に等しく設定されますが、t1の実際の容量はt2のサイズになります。したがって、copy-constructor / Assignment-operatorの後で要素をベクトルにプッシュし始めると、バッファオーバーランの問題が発生します。

次のように変更する必要があります

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));  
}
于 2011-03-01T19:29:52.483 に答える
1

このコードは私のためにコンパイルされませんでした。Clang は、114 行目 ( back() の実装) で「size」が呼び出されることを期待していると不満を漏らしていました。

行は「return buffer[size() -1];」を意図していたと思います。

また、このコンストラクターの実装に関する警告も表示されます: template Vector::Vector(unsigned int size, const T & initial)

最初の行はおそらく「my_size = size;」になるはずでした。(このコンストラクターの) 最後の行はおそらく削除する必要があります。

次に、209 行目のアサーションに失敗します。 assert(v3.size() == 1);

これはワームのかなりの缶を開きますが、明らかな問題は resize() の次の行にあります: "size = size;" これはおそらく「my_size = size;」を意味しています。

この変更により、231 行目 "v6.push_back(100);" から呼び出された push_back() の 121 行目でクラッシュするようになりました。

これは、reserve() の問題により失敗しています。メンバー変数と同じ名前のローカル変数「buffer」を作成しています。名前を temp_buffer に変更しましょう。注: assert() 実行時エラーは使用しないでください。assert() は論理エラー用です。この assert() は失敗できません。new は 0 を返しません。代わりにスローします。

reserve() で明らかな修正を行った後 (他にも問題があります)、main() の lin3 208 から呼び出された resize() からの呼び出しで、reserve() の copy() でクラッシュするようになりました。 1);".

容量を削減しているときに、予約が実際に新しいバッファーを割り当てていることがわかります。これは、パフォーマンスの損失と信頼性の損失の両方です (メモリ割り当てが失敗する可能性があります)。しかし、それでもクラッシュしてはならないので、明らかな設計上の欠陥に対処せずにクラッシュを防止しようとします。

コンテナに存在するすべてのアイテムを新しく割り当てられた配列にコピーしているため、クラッシュが発生しています。キャパシティを増やす必要がある場合にのみこれを行うのであれば、これは正しいでしょうが、この場合、新しいキャパシティが保持できるよりも多くのアイテムがあります。my_size がその値より大きい場合、コードは my_size を新しい容量に設定する必要があります。

これで、テスト コードは「成功」を報告します。

ただし、このコードにはまだ多くの問題があります。

最大の問題の 1 つは、割り当てられた配列で初期化されていないメモリを使用していないことです。これを行うことは std::vector の標準で必要とされており、パフォーマンスと信頼性の両方の利点があります。しかし、それはまたコードを複雑にするので、これは明らかに知的な課題を解決するための近道かもしれません。

コンストラクター: 初期化構文を使用して、データ メンバーを初期化します。

コピー コンストラクターと初期値からのコンストラクターを使用すると、ループ割り当てのいずれかが例外をスローすると、割り当てられた配列がリークします。

代入演算子は、「my_size」ではなく「my_capacity」のサイズの新しいバッファーを割り当てる必要がありますが、右側のオブジェクトのサイズが「this」オブジェクトよりも大きくない場合は、そうすべきではないという明らかな最適化があります。割り当てます。

代入演算子で新しい配列の割り当てが失敗した場合、バッファーは既に削除されているため、最終的には (Vector オブジェクトが破棄されると) バッファーが二重に削除され、その前にすべての地獄が解き放たれる可能性があります。

push_back() では、標準のパフォーマンス保証をサポートするために、既存の容量のサイズの定数分だけ容量を増やす必要があります。たとえば、「reserve(my_capacity * 1.5);」のようなものです。

于 2012-04-26T01:53:45.360 に答える
1

もしかして、このタイプミス?

Vector<T>::Vector(unsigned int size, const T & initial)
{
    my_size-size; 
于 2011-03-01T19:04:13.357 に答える
1

あなたの「準備」は壊れています。ローカル バッファには別の変数名を使用してください。

于 2011-03-01T19:07:57.087 に答える