我正在尝试在不使用迭代器的情况下实现我自己的vector类。以下是问题可能需要的部分。
template <typename T>
class Vector {
public:
...
~Vector()
{
delete [] m_data;
}
...
void erase(size_t position)
{
if (position >= m_size) {
throw std::out_of_range("erasing an element out of bounds");
}
--m_size;
for (size_t i = position; i < m_size; ++i) {
m_data[i] = m_data[i + 1];
}
m_data[m_size].T::~T();
}
...
private:
T* m_data;
size_t m_size;
...
};
以下是erase
对std::vector
m_data[position]
函数的引用:
这有效地减少了矢量大小,删除了元素的数量,之前调用了每个元素的析构函数。
所以我尝试通过调用最后一个重复元素的析构函数来实现相同的功能。 delete [] m_data
的析构函数是不必要的,因为它将被下一个元素替换。
问题是向量类{{1}}的析构函数中的代码也将调用每个元素的析构函数,这将导致内存的双重删除和崩溃。
任何人都可以帮助为我的矢量类编写正确的擦除函数吗?
答案 0 :(得分:2)
你在尝试的东西不容易飞!如果你想实现类似std::vector<T>
之类的东西,你需要完成整个monty:你需要处理原始内存并明确地构造/销毁对象。也就是说,您需要分配足够的未初始化内存块,根据需要在适当的位置构造/销毁对象,并最终释放分配的内存。这对于std::vector<T>
的玩具版本来说是一个有趣的练习,然后你很乐意使用你的编译器的版本运输,因为它设法更快,实际上实现了所有的功能,并且是合理的无bug。当然,如果您碰巧实现了标准C ++库的一个版本,那么您需要在整个练习中受到影响。好消息是:std::vector<T>
与std::deque<T>
相比是微不足道的,我会准备投入大笔资金,如果不使用算法,你将无法获得与标准库版本相同的效率(和为了获得这个效率,你需要相当复杂的算法版本;我不确定是否有很多实现实际上做了std::deque<T>
上好的专用版本。
不使用迭代器,BTW,只是没有帮助:像std::move()
(将迭代器作为参数的版本)或std::copy()
(如果你不使用C ++ 2011)这样的算法避免使用重复版本乱丢您的代码。在算法中使用代码具有额外的优点,即根据需要很好地封装它们并非完全无关紧要的逻辑。将重复需要的代码放入算法中使得容器的实现相对简单,使实现更加正确。 ...更不用说实施有趣的优化实际上也是可行的。
答案 1 :(得分:1)
你想要的是制作char数组。
然后你可以使用placement new将元素放入向量中,当删除元素时,显式调用析构函数。
void push_back(T const& el)
{
makeSureThereIsSpaceForOneMore();
new (&E[m_size]) T(el);
++m_size;
}
void erase(size_t index)
{
MoveStuffAround();
E(index)->~T();
--m_size;
}
private:
char* m_data;
size_t m_size;
...
T* E(size_t index){return reinterpret_cast<T*>(&m_data[index * sizeof(T)]);}
};
答案 2 :(得分:0)
@Artak不要忘记检查另一个方向(擦除)
答案 3 :(得分:0)
如何用m_data[m_size].T::~T();
替换m_data[m_size] = T();
?
答案 4 :(得分:0)
我提出了一个不使用new/delete
运算符的解决方案,而是使用C风格malloc/free
。这是因为new
和delete
运算符隐式调用构造函数和析构函数,并且您无法控制它们。相反,如果我们使用malloc/free
,那么我们可以在需要时显式调用对象的构造函数/ desctuctors。
因此,在这个例子中,我们可以显式地为析构函数调用erase
函数中的最后一个对象,也可以为向量类的desctuctor中的每个元素调用析构函数。