我最近写了一个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";
}
答案 0 :(得分:14)
以下是完整的源代码,从您的来源更新:
#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;
}
答案 1 :(得分:1)
也许是这个错字?
Vector<T>::Vector(unsigned int size, const T & initial)
{
my_size-size;
答案 2 :(得分:1)
你的“预备队”被打破了。使用另一个变量名称作为本地缓冲区。
答案 3 :(得分:1)
除了需要修复reserve
函数之外,copy-constructor和copy-assignment-operator还有一个有趣的问题:
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));
}
OR(如果你想让它重新调整为更小的数组)
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));
}
答案 4 :(得分:1)
这段代码没有为我编译。 Clang抱怨第114行(back()的实现)要求调用“size”。
我认为该行的目的是“返回缓冲区[size()-1];”
它还提供有关此构造函数的实现的警告: 模板 Vector :: Vector(unsigned int size,const T&amp; initial)
第一行可能应该是“my_size = size;” 应该删除最后一行(该构造函数)。
接下来它在第209行失败了断言:断言(v3.size()== 1);
这会打开很多蠕虫,但明显的问题是在行的resize():“size = size;”这可能是“my_size = size;”
通过此更改,我们现在在第121行崩溃,该行位于从第231行“v6.push_back(100)”调用的push_back()中;“
由于reserve()存在问题,这是失败的。我们正在创建一个与成员变量同名的局部变量“buffer”。我们将名称更改为temp_buffer。注意:不要断言()运行时错误。 assert()用于逻辑错误。这个断言()不会失败。新的永远不会返回0.它会反而投掷。
在reserve()中有明显的修复(还有其他问题)之后,我们现在在来自main()中的lin3 208调用的resize()的调用中的reserve()中崩溃,“v3 .resize(1);”
我们看到保留实际上是在减少容量时分配一个新的缓冲区。这既是性能损失又是可靠性损失(内存分配可能失败)。但我们仍然不应该崩溃,所以我们会尽力避免崩溃而不解决明显的设计缺陷。
崩溃即将到来,因为我们正在将容器中存在的所有项目复制到新分配的数组中。如果我们只是在需要增加容量时才这样做,这是正确的,但在这种情况下,我们有更多的项目,而不是我们的新容量。如果代码大于该值,则代码应将my_size设置为新容量。
现在测试代码报告“SUCCESS。”
但是此代码仍存在许多问题。
最大的问题之一是我们没有在分配的数组中使用未初始化的内存。这样做是std :: vector标准所要求的,它具有性能和可靠性方面的优势。但它也使代码复杂化,因此这可能是我们可以接受的捷径,显然是一种智力锻炼。
构造函数:使用初始化程序语法初始化数据成员。
使用初始值的复制构造函数和构造函数,如果任何循环赋值抛出异常,您将泄漏已分配的数组。
赋值运算符应该分配一个大小为“my_capacity”而不是“my_size”的新缓冲区,尽管有一个明显的优化,如果右侧对象的大小不大于“this”对象,我们不应该根本就没有分配。
如果在赋值运算符中分配新数组失败,我们已经删除了缓冲区,所以我们最终(当我们的Vector对象被销毁时)有一个双重删除的缓冲区,我们可能已经完全崩溃了然后
在push_back()中,为了支持标准的性能保证,我们需要将容量增加现有容量的一小部分。例如:“reserve(my_capacity * 1.5);”