尝试在矢量类中创建复制功能
Posted
技术标签:
【中文标题】尝试在矢量类中创建复制功能【英文标题】:Trying to create copy function in a vector class 【发布时间】:2014-10-17 12:49:53 【问题描述】:我正在实现一个向量类,但不知道如何编写一个函数来将一个向量复制到另一个向量中。
template <class T> class Vec
public:
//TYPEDEFS
typedef T* iterator;
typedef const T* const_iterator;
typedef unsigned int size_type;
//CONSTRUCTOS, ASSIGNMENT OPERATOR, & DESTRUCTOR
Vec() this->create();
Vec(size_type n, const T& t = T()) this->create(n, t);
Vec(const Vec& v) copy(v);
Vec& operator=(const Vec& v);
~Vec() delete [] m_data;
//MEMBER FUNCTIONS AND OTHER OPERATORS
T& operator[] (size_type i) return m_data[i];
const T& operator[] (size_type i) const return m_data[i];
void push_back (const T& t);
iterator erase(iterator p);
void resize(size_type n, const T& fill_in_value = T());
void clear() delete [] m_data; create();
bool empty() const return m_size == 0;
size_type size() const return m_size;
//ITERATOR OPERATIONS
iterator begin() return m_data;
const_iterator begin() const return m_data;
iterator end() return m_data + m_size;
const_iterator end() const return m_data + m_size;
private:
//PRIVATE MEMBER FUNCTIONS
void create();
void create(size_type n, const T& val);
void copy(const Vec<T>& v);
//REPRESENTATION
T *m_data; //point to first location inthe allocated array
size_type m_size; //number of elements stored in the vector
size_type m_alloc; //number of array locations allocated, m_size <= m_alloc
;
//create an empty vector (null pointers everywhere)
template <class T> void Vec<T>::create()
m_data = NULL;
m_size = m_alloc = 0; //no memory allocated yet
//create a vector with size n, each location having the given value
template <class T> void Vec<T>::create(size_type n, const T& val)
m_data = new T[n];
m_size = m_alloc = n;
for (T* p = m_data; p != m_data + m_size; ++p)
*p = val;
//assign one vector to another, avoiding duplicate copying
template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& v)
if (this != &v)
delete [] m_data;
this -> copy(v);
return *this;
这是我想到的第一件事:
template <class T> void Vec<T>::copy(const Vec<T>& v)
m_size = m_alloc = v.size();
m_data = &v;
我收到关于不兼容类型的错误...好吧,它们不兼容是有道理的。所以我取出'const',现在它可以工作了。
template <class T> void Vec<T>::copy(Vec<T>& v)
m_size = m_alloc = v.size();
m_data = &v[0];
我猜这不是完全正确或好的形式。我不知道。现在我得到一个关于指针被释放但没有被分配的错误(但它至少现在可以成功编译、运行和复制向量)。所以我想说我并不真正理解通过引用传递变量/数组/向量/事物,以及内存的动态分配。我的问题是:如何改进我编写的复制函数,以不比较两个不兼容的变量,或者成功地将指针动态分配给新向量,以免出现 malloc 错误?
【问题讨论】:
您无需致电size()
。在复制构造函数中,您可以访问参数的私有变量,所以m_size = m_alloc = v.m_size
就可以了。其次,不要做m_data = &v;
。您应该复制缓冲区,而不是参考。
你所做的不是复制一个向量,你是共享>一个向量(这可能会在以后导致双释放和分段错误)。如果您想实际复制一个向量,您需要分配内存并复制所有元素(例如,std::copy
)。
如果你真的想复制一个向量,你可以简单地清除this
,然后在循环中为向量v
中的每个元素调用push_back()。
您的operator=
有问题。您删除了m_data
,因此如果尝试再次分配内存时抛出异常,您的this
已损坏。
【参考方案1】:
你需要对元素进行深拷贝,而不是简单地分配指针m_data
:
// precondition: `m_data` is not allocated
template <class T> void Vec<T>::copy(const Vec<T>& v)
m_data = new T[v.size()];
m_size = m_alloc = v.size();
for (size_t ii = 0; ii < m_size; ++ii)
m_data[ii] = v[ii];
【讨论】:
@jld 这个答案有一个小问题,那就是如果new
抛出异常,this
的m_size
和m_alloc
成员将被冲洗掉。分配应该发生在任何其他行之前,这样异常就不会导致问题。【参考方案2】:
请注意,前面给出的答案存在异常安全问题。简单的解决方法是先分配。
// precondition: `m_data` is not allocated
template <class T> void Vec<T>::copy(const Vec<T>& v)
m_data = new T[v.size()];
m_size = m_alloc = v.size();
for (size_t ii = 0; ii < m_size; ++ii)
m_data[ii] = v[ii];
您的代码的另一个问题是operator=
,它不是异常安全的。在使用 new[]
再次分配之前,您删除了 m_data
。如果new[]
失败,则您的对象已损坏。
一旦您对copy
进行了上述修复,那么operator =
就可以用复制构造函数来编写:
template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& v)
// construct a temporary
Vec<T> temp = v;
// set the members
m_size = m_alloc = temp.size();
delete [] m_data;
m_data = temp.m_data;
// Now set temp.m_data to 0, so that destruction of temp doesn't delete
// our memory
temp.m_data = 0;
return *this;
基本上,我们从v
构造一个临时的,删除this->m_data
,然后将temp
的元素分配给this
。然后我们通过将temp.m_data
数据设置为NULL 来删除temp
的内容。需要这样做,以便当temp
死亡时,我们不希望delete
分配给this
的数据。
注意如果第一行Vec<T> temp = v;
抛出异常,对this
无害的是
完成,因此提供了异常安全性。
这是Captain Giraffe
建议的复制/交换习语:
template <class T> class Vec
//...
void swap(Vec<T>& left, Vec<T>& right);
//..
;
template <class T> void Vec<T>::swap(Vec<T>& left, Vec<T>& right)
std::swap(left.m_size, right.m_size);
std::swap(left.m_alloc, right.m_alloc);
std::swap(left.m_data, right.m_data);
template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& v)
// construct a temporary
Vec<T> temp = v;
swap(*this, temp);
return *this;
这里的区别在于我们将temp
的成员替换为this
。因为temp
现在将包含this
曾经拥有的指针,所以当temp
死亡时,它将在这个“旧”数据上调用delete
。
【讨论】:
为什么不复制和交换?惯用的、安全的、优雅的和重用现有代码。 temp什么时候死?是什么决定的? @jld -When will temp die? What determines that?
这就是析构函数的美妙之处。当temp
超出范围时(即在函数终止处,即最终的
),将调用temp
的析构函数。【参考方案3】:
为了让您的复制构造函数安全,它需要在无法复制时失败。
Vec<T>::Vec(const Vec<T>& o):m_size(o.m_size), m_alloc(o.m_size), m_data(new T())
std::copy( o.m_data, o.m_data+o.m_size, m_data);
复制构造函数应该能够替换任何 Vec
通过引入交换功能可以轻松处理分配。这是异常安全的。
void Vec<T>::swap(Vec<T>& rhs)
std::swap(m_data, rhs.m_data);
std::swap(m_size, rhs.m_size);
std::swap(m_capacity, rhs.m_capacity);
除了安全复制&交换&习语它变成:
Vec<T>& Vec<T>::operator = (const Vec<T>& rhs)
Vec<T> cpy=rhs;
swap( this, cpy);
return *this;
【讨论】:
以上是关于尝试在矢量类中创建复制功能的主要内容,如果未能解决你的问题,请参考以下文章