向量类中擦除函数的正确实现是什么

What is the correct implementation of erase function in vector class

本文关键字:实现 是什么 函数 擦除 向量      更新时间:2023-10-16

我正在尝试在不使用迭代器的情况下实现我自己版本的向量类。以下是这个问题可能需要的部分。

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;
    ...
};

以下是cplusplus.com对std::vectorerase函数的引用:

这有效地通过移除元素的数量来减少向量大小,在之前调用每个元素的析构函数。

因此,我试图通过调用最后一个重复元素的析构函数来实现相同的功能。m_data[position]的析构函数是不必要的,因为它将被下一个元素所取代。

问题是向量类delete [] m_data的析构函数中的代码也会为每个元素调用析构函数,这将导致内存的双重删除和崩溃。

有人能帮我为向量类写正确的擦除函数吗?

你正在尝试的不会那么容易!如果你想实现类似std::vector<T>的东西,你需要做完整的工作:你需要处理原始内存并显式地构造/销毁对象。也就是说,您需要分配足够的未初始化内存块,根据需要在适当的位置构造/销毁对象,并最终释放分配的内存。对于玩具版的std::vector<T>来说,这是一个有趣的练习,然后您会很乐意将版本随编译器一起使用,因为它在某种程度上更快,实际上实现了所有功能,并且相当无错误。当然,如果您碰巧实现了标准C++库的一个版本,那么您将需要经历整个练习。好消息是:与std::deque<T>相比,std::vector<T>微不足道,我敢打赌,如果不使用算法,你将无法获得与标准库版本一样高效的版本(要想真正有效地实现这一点,还需要相当复杂的算法版本;我不确定是否有许多实现真正实现了std::deque<T>上的专业版本)。

顺便说一句,不使用迭代器是没有帮助的:像std::move()(以迭代器为参数的版本)或std::copy()(如果你不使用C++2011)这样的算法可以避免代码中出现重复版本。将代码放入算法中还有一个额外的优势,即它们不是完全琐碎的逻辑,而是根据需要很好地封装的。将重复需要的代码放入算法中会使容器的实现相对简单,从而使实现更有可能正确。。。更不用说实现有趣的优化实际上也是可行的。

您想要的是生成char数组。

然后,您可以使用placementnew将元素放入向量中,并在删除元素时显式调用析构函数。

   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)]);}
};

@Artak别忘了检查另一个方向(擦除)的边界

m_data[m_size] = T();替换m_data[m_size].T::~T();怎么样?

我提出了一个解决方案,不使用new/delete运算符,而是使用C样式的malloc/free。这是因为newdelete运算符隐式调用构造函数和析构函数,而您无法控制它们。相反,如果我们使用malloc/free,那么当我们需要时,我们可以显式地调用对象的构造函数/描述符

因此,在这个例子中,我们可以显式地调用erase函数中最后一个对象的析构函数,也可以显式调用向量类的解构函数中的每个元素的析构因子。