矢量 class 的简单实现类似于 std::vector

Simple implementation of vector class like std::vector

我正在实现一个像 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. 如果发生异常,您的程序将以正常状态结束。哪个未指定。
  2. 如果发生异常,您的程序将以原始状态结束。

您面临的主要挑战是处理赋值和复制构造函数抛出问题。正如评论已经指出的那样,您不应该使用 memcpy 因为它无法调用复制构造函数。复制 std::string 也应该复制字符缓冲区,例如,字符串向量是您应该支持的完全正常的类型。

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

现在,异常安全意味着您让程序处于正常状态,因此没有内存泄漏。你的 vector 的复制 ctor 失败了,所以 dtor 不会 运行。那谁来清理T* array?这必须在您的复制 ctor 中完成。

复制失败时,不会有新的vector,所以你免费获得第二类异常安全。 ("strong exception safety")。但是接下来让我们看一下赋值运算符,v2 = v1。您将覆盖一个旧矢量。如果你先做一个.resize(0),然后复制所有元素,你可能会在复制到一半时遇到异常。你原来的矢量内容没有了,新的内容不完整。不过,您还没有泄漏任何内存,也没有复制半个元素。

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

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

您需要检查所有方法以查看是否会出现这些问题,但模式通常是相似的。不要泄漏 array 如果元素复制或元素分配抛出异常,请将该异常传播给调用者。