像std :: vector这样的矢量类的简单实现

时间:2015-02-25 13:44:35

标签: c++ vector stl

我正在实现一个像std :: vector这样的简单向量,我编写了一些函数,而不用担心它给出了什么样的异常安全保证。我对c ++的例外有一点了解,但我没有经历过编写异常安全代码。 这是我的代码:

template <typename T>
class vector {
public:
   vector(int _DEFAULT_VECTOR_SIZE = 10) :
       size(0), array_(new T[_DEFAULT_VECTOR_SIZE]), capacity(_DEFAULT_VECTOR_SIZE)  {}
  void push_back(T& elem)
  {
    if (size == capacity)
    resize(2 * size);
    array_[size] = elem;
    size++;
  }

  void pop_back()
  {
     --size;
  }

  void resize(int size_)
  {
     if (size_ > capacity)
    {
      T* temp = new T[size_];
      memcpy(temp,array_,size*sizeof(T));
      swap(temp, array_);
      delete[] array_; 
      capacity = size_;
     }
  }

  private:
    T* array_; 
    int size;
    int capacity;
 };

所以我的问题是:我如何修改我的代码(函数),至少提供基本保证,或者为基本或强保证编写异常安全代码的一些技巧? 感谢

1 个答案:

答案 0 :(得分:1)

异常安全有两种主要形式:

  1. 如果发生异常,您的程序将以合理的状态结束。哪一个未指定。
  2. 如果发生异常,您的程序将以原始状态结束。
  3. 您面临的主要挑战是处理赋值和复制构造函数。正如评论已经注意到的那样,您不应该使用memcpy,因为它无法调用复制构造函数。例如,复制std::string也应该复制字符缓冲区,字符串向量是一个非常正常的类型,你应该支持它。

    那么,让我们看看你的vector的拷贝构造函数。它需要复制源向量的每个元素。每个单独的副本都可以抛出。如果其中一个字符串太长而副本抛出std::bad_alloc怎么办?

    现在,异常安全意味着您将程序保持在一个理智的状态,因此没有内存泄漏。您的矢量副本ctor失败,因此dtor将无法运行。谁清理了T* array呢?这必须在您的副本中完成。

    当复制失败时,将不会有新的向量,因此您可以免费获得第二种类型的异常安全性。 (“强烈的异常安全”)。但是让我们看看下面的赋值运算符v2 = v1。你会覆盖一个旧的矢量。如果您先执行.resize(0)然后复制所有元素,则可能会在副本中途遇到异常。您的原始矢量内容已消失,新内容不完整。尽管如此,你还没有泄露任何记忆,也没有复制半个元素。

    为了使分配安全,有一个简单的技巧:首先将源向量复制到临时向量。如果失败,没问题(见上文)。我们还没有碰到目的地。但是如果赋值成功,我们会交换临时和目标向量的array*指针sizecapacity。交换指针和交换int是安全的(不能抛出)。最后,我们让临时向量超出范围,这会破坏不再需要的旧向量元素。

    因此,通过对临时对象进行所有危险操作,我们确保任何异常都不会触及原始状态。

    您需要检查所有方法以查看是否可能出现这些问题,但模式通常类似。如果元素副本或元素赋值抛出,请不要泄漏array,请将该异常传播给调用者。