C++ 向量';s使用memcpy实现push_-back

C++ 向量';s使用memcpy实现push_-back,c++,C++,给定以下回推代码: template <typename T> void Vector<T>::push_back(const T& item) { if (_size == _capacity) { _capacity = _capacity + (_capacity > 1 ? (_capacity / 2) : 1); T* newVec = new T[_capacity]; memcpy(newVec, _ptr, _si

给定以下回推代码:

template <typename T>
void Vector<T>::push_back(const T& item) {

if (_size == _capacity) {
    _capacity = _capacity + (_capacity > 1 ? (_capacity / 2) : 1);
    T* newVec = new T[_capacity];
    memcpy(newVec, _ptr, _size*(sizeof(T)));
    delete [] _ptr;
    _ptr = newVec;
}
_ptr[_size++] = item;
}
这种实现安全吗。。?即使T是多态类型,memcpy也能正确地完成他的工作吗

希望听到一些关于如何改进实现的建议。

不要使用
std::memcpy
只能在普通可复制对象上使用
std::memcpy
。否则,这是未定义的行为

但是,您可以手动复制所有元素。适用,因为它可能专门用于普通类型:

实际上,
std::copy
的实现避免了多次赋值,如果值类型是可复制的,则使用批量复制函数,如
std::memcpy


想象一下,如果
T
本身就是一个
向量
,会发生什么

现在有两个向量指向同一个缓冲区,它们都将删除缓冲区。。。坏主意


(从技术上讲,从你
memcpy
的那一刻起,这是一种未定义的行为。我刚刚给了你一个最可能的结果。)

这通常是不安全的-但是C++11提供了:

#包括
...
if(std::is_琐碎地_可复制::值)
//*可以*使用memcpy。。。

这是不安全的,例如,如果
T
正在这样做:

struct T
{
    T* myself;
    T() : myself(this) {}
    void foo() { myself->bar(); }
    void bar() { ... }
};

由于您仅通过移动对象的内存而不调用构造函数/析构函数来移动对象的内存位置,
我自己
将不会更新,并且当您随后调用
foo
时,它将使用无效的
指针调用
bar

如果
T
具有副本构造函数,
memcpy
不会调用它,您有一个问题。请注意,向量只能包含相同类型的对象。它无法保存不同多态类型的元素,因此无需担心多态类型的工作是否正常。除了您要问的问题,
std::vector
不使用
new t[]
来分配内存。首先,因为它默认使用
Alloc
模板参数或
std::allocator
,使其更灵活。其次,因为
newt[]
将调用每个元素上的
T
默认构造函数,向量不允许这样做,因为
vector
接口不要求元素类型
T
必须是默认可构造的,才能使用
push\u
。鉴于
std::vector
已经投入了大量的工作,您可能可以通过遵循此设计改进
vector
。-)那么,你说我应该使用malloc吗?@Rouki:不,我说你应该使用
std::allocator
Alloc
模板参数。谢谢你的示例。知道了。有什么提高安全性的好办法吗?@Rouki你可以使用
std::move(\u ptr,\u ptr+\u size,newVec)
这应该是安全的,但效率不高。为了提高效率,您需要分配原始内存并移动构造,然后只默认构造新元素。如果它不是简单的可复制的呢?@Rouki-那么您就不能使用memcpy。最快的memcpy替换方法是?@Rouki-问题是,您在数组中的每个元素上调用默认构造函数,然后想复制。您可以使用
:operator new(size\u t)
查看和分配。如果它不是一个普通的可复制对象呢?在这种情况下我该怎么办?@Rouki,但即使这样,您的实现(OPs之一)也不正确,因为您在不调用析构函数的情况下覆盖了已经默认构造的值。@AaronMcDaid:Err,
new[]
调用
T::T()
\u ptr[0…\u size)
包含原始元素,
\u ptr[\u size…\u capacity)
默认构造,因此没有“混淆”但是,这个版本的
.push_back
需要
T
来默认构造,这确实可以通过使用placement
new
@Zeta来避免,你是对的。我删除了我的评论。我想的是
malloc
!为什么?他没有显示他的复制构造函数,我们可以假设它实现得很好。你为什么这么想
T
和current vector指向同一个缓冲区?--他实现的是
push_back
而不是一个复制或赋值方法。@MM.:the
memcpy()
将类型T元素的数组复制到一个新的内存块(使用原始内存副本,而不是T的构造函数之一)。例如,当时
newVec[0].\u ptr
\u ptr[0]。\u ptr
将为它们自己的元素指向同一个内存块。当执行
delete[]\u ptr
时,
newVec[0]。\u ptr
将持有一个过时的指针。@MichaelBurr:想得更多,是的,你是对的。我以为他不是在谈论memcpy的问题。谢谢。
template <typename T>
void Vector<T>::push_back(const T& item) {
  if (_size == _capacity) {
      size_t new_cap = _capacity > 0 ? 2 * _capacity : 2;    
      T * newVec = new T[new_cap];
      std::copy(_ptr, _ptr + _size, newVec);
      std::swap(_capacity, new_cap);
      std::swap(_ptr, newVec);
      delete[] newVec;
  }
  _ptr[_size++] = item;
}
template <typename T, class Allocator = std::allocator<T> >
class Vector{
public:
  typedef typename Allocator::pointer pointer;
  typedef typename Allocator::size_type size_type;

  Vector() : _ptr(0), _capacity(0), _size(0){}
  ~Vector() {
    if(_capacity == 0)
      return;
    while(_size > 0)
      pop_back();
    _alloc.deallocate(_ptr, _capacity);
  }

  void reserve(size_type new_cap){
    if(new_cap <= _capacity)
      return;

    // allocate memory
    T * tmp = _alloc.allocate(new_cap);

    // construct objects
    for(unsigned int i = 0; i < _size; ++i){
      _alloc.construct(tmp + i, _ptr[i]); // or std::move(_ptr[i])
    }

    // finished construction, save to delete old values
    for(unsigned int i = 0; i < _size; ++i){
      _alloc.destroy(_ptr + i);
    }

    // deallocate old memory
    _alloc.deallocate(_ptr, _capacity);
    _ptr = tmp;
    _capacity = new_cap;
  }

  void push_back(const T& val){
    if(_size == _capacity)
      reserve(_capacity > 0 ? 2 * _capacity : 1);    
    _alloc.construct(_ptr + _size, val);
    _size++; // since T::T(..) might throw
  }

  void pop_back(){
    _alloc.destroy(_ptr + _size - 1);
    _size--;    
  }

  T& operator[](size_type index){
    return _ptr[index];
  }

private:
  pointer _ptr;
  size_type _capacity;
  size_type _size;
  Allocator _alloc;
};
#include <type_traits>
...
if (std::is_trivially_copyable<T>::value)
    // *can* use memcpy...
struct T
{
    T* myself;
    T() : myself(this) {}
    void foo() { myself->bar(); }
    void bar() { ... }
};