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
来默认构造,这确实可以通过使用placementnew
@Zeta来避免,你是对的。我删除了我的评论。我想的是malloc
!为什么?他没有显示他的复制构造函数,我们可以假设它实现得很好。你为什么这么想T
和current vector指向同一个缓冲区?--他实现的是push_back
而不是一个复制或赋值方法。@MM.:thememcpy()
将类型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() { ... }
};