尝试在矢量类中创建复制功能

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 = &amp;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 抛出异常,thism_sizem_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-&gt;m_data,然后将temp的元素分配给this。然后我们通过将temp.m_data 数据设置为NULL 来删除temp 的内容。需要这样做,以便当temp 死亡时,我们不希望delete 分配给this 的数据。

注意如果第一行Vec&lt;T&gt; 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::copy 成员。

通过引入交换功能可以轻松处理分配。这是异常安全的。

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;

【讨论】:

以上是关于尝试在矢量类中创建复制功能的主要内容,如果未能解决你的问题,请参考以下文章

BIMBIMFACE中创建矢量文本

我想要两个在一个按钮中创建两个功能

在 MEAN 堆栈中创建搜索文章功能

在所有视图控制器中创建警报功能 - swift

如何在 C# 中创建一个临时文件(用于写入)? [复制]

Flutter:如何在我的 Style 类中创建构造函数? [复制]