如何在 C++ 模板容器中实现 erase() 方法
Posted
技术标签:
【中文标题】如何在 C++ 模板容器中实现 erase() 方法【英文标题】:How to implement erase() method in a C++ template container 【发布时间】:2015-05-09 18:47:44 【问题描述】:对于书本练习,我需要创建一个名为 Vec 的简单 C++ 容器(模仿 std::vector 的容器)。但是,我在实现一个简单的 erase() 方法时遇到了问题。我的意图是销毁该索引中的对象,然后将索引之后的所有元素移回列表的下方。
PS:我是 C++ 新手,对 C++ 中的内存管理知之甚少。
Vec.h
#pragma once
#ifndef GUARD_VEC_H
#define GUARD_VEC_H
#define WARNING D_SCL_SECURE_NO_WARNINGS
#define WARNING_ID 4996
#pragma message (WARNING)
#pragma warning (disable: WARNING_ID)
#define MAX(a , b) ((a > b) ? a : b)
#define MIN(a , b) ((a < b) ? a : b)
#include <memory> //For: Allocator
#include <cstddef> //FOR: size_type
template<class T> class Vec
public: //interface
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec() create();
explicit Vec(size_type n, const T& val = T()) create(n, val);
Vec(const Vec& v) create(v.begin(), v.end());
Vec& operator = (const Vec&);
~Vec() uncreate();
T& operator[] (size_type i) return data[i];
const T& operator[] (size_type i) const return data[i];
void push_back(const T& t)
if (avail == limit)
grow();
unchecked_append(t);
void clear();
bool erase(size_type i);
size_type size() const return avail - data;
iterator begin() return data;
const_iterator begin() const return data;
iterator end() return avail;
const_iterator end() const return avail;
private: //implementation
iterator data;
iterator avail;
iterator limit;
std::allocator<T> alloc;
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
void uncreate();
void grow();
void unchecked_append(const T&);
;
template <class T> bool Vec<T>::erase(size_type i)
//No, doesn't work at all
if (i > size())
return false;
alloc.destroy(data+ i);
return true;
//Implement move-back
template <class T> void Vec<T>::clear()
uncreate(); //Destroys all objects and deallocates all addresses
grow(); //Allocates addresses for next use.
template <class T> void Vec<T>::create()
data = avail = limit = 0;
template <class T> void Vec<T>::create(size_type n, const T& val)
data = alloc.allocate(n);
limit = avail = data + n;
std::uninitialized_fill(data, limit, val);
template <class T>
void Vec<T>::create(const_iterator i, const_iterator j)
data = alloc.allocate(j - i);
limit = avail = std::uninitialized_copy(i, j, data);
template <class T> void Vec<T>::uncreate()
if (data)
iterator it = avail;
while (it != data)
alloc.destroy(--it);
alloc.deallocate(data, limit - data);
data = limit = avail = 0;
template <class T> void Vec<T>::grow()
size_type new_size = MAX(2 * (limit - data), ptrdiff_t(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = std::uninitialized_copy(data, avail, new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = data + new_size;
template <class T> void Vec<T>::unchecked_append(const T& val)
alloc.construct(avail++, val);
template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& rhs)
//rhs = right hand side
if (&rhs != this)
uncreate();
create(rhs.begin(), rhs.end());
return *this;
#endif
main.cpp
#include "Vec.h"
#include <iostream>
#include "Windows.h"
#include <string>
int main()
Vec<double> vector;
for (int i = 0; i < 10; i++)
vector.push_back((double) i);
//Check Copy Constructor
Vec<double> vector2 = vector;
Vec<double> vector3;
Vec<double> vector4;
for (int i = 0; i < 10; i++)
vector3.push_back((double)(i*2));
//Check Assignment Operator
vector4 = vector3;
for (int i = 0; i < 10; i++)
std::cout << vector[i] << " " << vector2[i] << " " << " " << vector3[i] << " "
<< vector4[i] << std::endl;
//Check Erase Operator
vector.erase(3);
for (int i = 0; i < vector.size(); i++)
std::cout << vector[i] << std::endl;
std::system("PAUSE");
return 0;
主要,“矢量”是我正在测试的对象。它被初始化为数字 0 到 9,并且在调用 vector.erase(3) 后保持不变;
另外,关于迭代器的解释,data 指向列表的开头(第一个元素),avail 指向初始化元素的结束位置,limit 指向未初始化存储结束的位置。数据
【问题讨论】:
附带问题:MAX
不需要宏。使用std::max
或自己编写一个模板函数,但避免使用宏。
编译器错误?意外行为?介意explain more?
没有错误,只有矢量是“不变的”
仅仅因为你破坏了对象并不意味着它从向量中消失了。同样的原因你仍然可以在SomeClass c; c.~SomeClass();
之后访问c
(但不要那样做)。
【参考方案1】:
这似乎是家庭作业,所以这不是一个完整的答案。
不过我会给出提示。
您最初对解决方案的猜测存在很大问题。一旦您销毁了data[i]
,该位置现在(或应该被视为)不能用作T
类型的对象。特别是不能使用data[i] = data[i+1]
,因为赋值运算符的知识可能已经被破坏了。
标准补救措施适用:那就不要那样做。
您已经发现需要将元素 i
上方的每个元素向下移动一个。所以在你摧毁任何东西之前,先这样做。 (这是一个大提示。)当你完成后,很可能有一些东西需要销毁(另一个大提示)。
顺便说一句,我上面暗示的解决方案不是很有效。在 C++11 中有更有效的方法来做到这一点。
【讨论】:
这段代码可以作为另一种解决方案吗?它似乎可以完成这项工作,但不确定是否存在任何背景问题。avail = std::uninitialized_copy(data + index, avail, data + index-1);
【参考方案2】:
这是我如何让擦除功能工作的:
template <class T> bool Vec<T>::erase(size_type i)
if (i > size())
return false;
//alloc.destroy(data + i); => not necessary, assuming T has a proper assignment operator defined
for (int i = 0; i < size() - 1; i++)
data[i] = data[i + 1];
avail--; //decrease the size of the vector
alloc.destroy(data + size());
return true;
您可能想查看Difference between "destroy" "destructor" "deallocate" in std::allocator? 以了解 allocator.destroy 的作用。
【讨论】:
这不是答案。它调用未定义的行为。一旦销毁,语句data[i] = data[i+1]
会调用未定义的行为。
我没有说拷贝构造函数。我解决了您对data[i] = data[i+1]
的使用问题。一旦 data[i]
被销毁,这是未定义的行为。
更新后的解决方案还是有一些问题。 (1) 使用data[i]=data[i+1]
对于超出向量可用部分末尾的元素是无效的(未定义的行为)。你的循环太高了。 (2) 如果你解决了这个问题,这种方法可能会泄漏资源。向量的最后一个可用元素 (data[avail-1]
) 将在其上方有一个副本。以上是关于如何在 C++ 模板容器中实现 erase() 方法的主要内容,如果未能解决你的问题,请参考以下文章
尝试在 Python 中重写 C++ 代码时出现问题:删除地图中的项目和“vector.erase(vector.end())”