避免在数组订阅运算符中悬空引用

时间:2013-09-24 06:38:32

标签: c++ arrays vector

如何在下面的某个向量实现中避免数组订阅运算符中的悬空引用?如果realloc更改指针,则先前从operator []获取的引用不再有效。我不能使用new / delete。我必须使用malloc / realloc / free

template <class T>
class Vector
{
  public:
  typedef size_t size_type_;

  ...

  T& operator[] (const size_type_);
  void push_back (const T&);

  ...

  private:
  const size_type_ page_size_;
  size_type_ size_;
  size_type_ capacity_;
  T* buffer_;
};


template<class T>
inline
T&
some_namespace::Vector<T>::operator[] (const size_type_ index)
{
  ASSERT(index < size_);
  return buffer_[index];
}


template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
  if (size_ >= capacity_)
  {
    capacity_ += page_size_;
    T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));

    if (temp != NULL)
    {
      buffer_ = temp;
    }
    else
    {
      free(buffer_);
      throw some_namespace::UnexpectedEvent();
    }
  }

  buffer_[size_++] = val;
}

顺便说一句,代码中悬空引用的来源是:

v_.push_back(v_[id]);

其中v_是Vector的实例。为了防止这种情况,新的push_back是:

template<class T>
inline
void
some_namespace::Vector<T>::push_back(const T& val)
{
  if (size_ >= capacity_)
  {
    const T val_temp = val; // copy val since it may come from the buffer_
    capacity_ += page_size_;
    T* temp = (T*)(realloc(buffer_, capacity_*sizeof(T)));

    if (temp != NULL)
    {
      buffer_ = temp;
    }
    else
    {
      free(buffer_);
      throw some_namespace::UnexpectedEvent();
    }

    buffer_[size_++] = val_temp;
  }
  else
  {
    buffer_[size_++] = val;
  }
}

1 个答案:

答案 0 :(得分:4)

基本上你可以做三件事:

  • 按原样接受并记录。这就是STL的作用,而且实际上非常合理。
  • 不要返回引用,返回副本。当然,这意味着您不能再分配给向量的元素(v[42] = "Hello")。在这种情况下,您需要一个operator[]标记为const
  • 返回一个代理对象,它存储对向量本身和索引的引用。您可以从const T&向代理添加一个赋值,以允许写入向量的元素,并且您需要提供一些访问/读取值的方法,很可能是隐式operator const T&。由于代理对象在每次访问上询问向量以获取元素的当前位置,因此即使向量在调用之间更改了这些元素的位置,这仍然有效。请注意多线程。

这是代理的草图,未经测试且不完整:

template<typename T> class Vector
{
private:
    // ...

    // internal access to the elements
    T& ref( const size_type_ i );
    const T& ref( const size_type_ i ) const;

    class Proxy
    {
    private:
        // store a reference to a vector and the index
        Vector& v_;
        size_type_ i_;

        // only the vector (our friend) is allowed to create a Proxy
        Proxy( Vector& v, size_type_ i ) : v_(v), i_(i) {}
        friend class Vector;

    public:
        // the user that receives a Proxy can write/assign values to it...
        Proxy& operator=( const T& v ) { v_.ref(i_) = v; return *this; }

        // ...or the Proxy can convert itself in a value for reading
        operator const T&() const { return v_.ref(i_); }
    };

    // the Proxy needs access to the above internal element accessors
    friend class Proxy;

public:
    // and now we return a Proxy for v[i]
    Proxy operator[]( const size_type_ i )
    {
        return Proxy( *this, i );
    }
};

请注意,上述内容不完整,整个技术存在一些缺点。最重要的问题是API中的代理“泄漏”,因此不符合某些用例。您需要根据您的环境调整技术,看它是否适合。