2

我很惊讶在调用 erase 时发现了 vector::erase 移动元素。我认为它会将最后一个元素与“待删除”元素交换并将大小减小一。我的第一反应是:“让我们扩展 std::vector 并覆盖 erase()”。但是我在许多线程中发现,例如“从 C++ STL 容器派生是否有任何真正的风险? ”,它可能导致内存泄漏。但是,我没有向向量添加任何新的数据成员。所以没有额外的内存可以释放。是否还有风险?

有人建议我们应该更喜欢组合而不是继承。在这种情况下,我无法理解这个建议。为什么我要把时间浪费在包装原本很棒的 std::vector 类的每个函数的“机械”任务上?继承确实对这项任务最有意义——或者我错过了什么?

4

4 回答 4

11

为什么不写一个独立的函数来做你想做的事:

template<typename T>
void fast_erase(std::vector<T>& v, size_t i)
{
   v[i] = std::move(v.back());
   v.pop_back(); 
}

不过,这一切都归功于塞思·卡内基。我最初使用“std::swap”。

于 2012-04-14T20:35:19.877 回答
4

微妙的问题。您要打破的第一条准则是:“继承不是为了代码重用”。第二个是:“不要从标准库容器继承”。

但是:如果你能保证,没有人会把你unordered_vector<T>当作vector<T>你的好人。但是,如果有人这样做,结果可能是未定义的和/或可怕的,无论您有多少成员(它可能看起来很完美,但仍然是未定义的行为!)。

您可以使用private继承,但这不会使您免于编写包装器或使用大量语句提取成员函数,using这几乎与组合一样多的代码(尽管少一点)。


编辑:我对using陈述的意思是:

class Base {
  public:
    void dosmth();
};

class Derived : private Base {
  public:
    using Base::dosmth;
};

class Composed {
  private:
    Base base;
  public:
    void dosmth() {return base.dosmth(); }
};

您可以使用std::vector. 如您所见Derived,代码明显少于Composed.

于 2012-04-14T20:39:00.390 回答
3

继承的风险在以下示例中:

std::vector<something> *v = new better_vector<something>();
delete v;

这会导致问题,因为您删除了指向没有虚拟析构函数的基类的指针。
但是,如果您总是删除指向您的类的指针,例如:

better_vector<something> *v = new better_vector<something>();
delete v;

或者不要在堆上分配它没有危险。只是不要忘记在你的析构函数中调用父析构函数。

于 2012-04-14T20:32:26.897 回答
2

我认为它会将最后一个元素与“待删除”元素交换并将大小减小一。

vector::erase 保持元素的顺序,同时将最后一个元素移动到已擦除的元素,并且不会将大小减小一。由于 vector 实现了数组,因此没有 O(1) 方法来保持元素的顺序并同时擦除(除非您删除最后一个元素)。

如果维护元素的顺序并不重要,那么您的解决方案就可以了,否则,您最好使用其他容器(例如list,它实现了双向链表)。

于 2012-04-14T20:52:19.023 回答