我很惊讶地发现调用擦除时的vector :: erase移动元素。我认为它会将最后一个元素与“待删除”元素交换,并将大小减小一个。我的第一反应是:“让我们扩展std :: vector并覆盖erase()”。但我发现许多线程如“Is there any real risk to deriving from the C++ STL containers?”,它可能导致内存泄漏。但是,我没有向vector添加任何新的数据成员。因此没有额外的内存可以释放。还有风险吗?
有些人认为我们应该更喜欢构成而不是继承。在这种情况下,我无法理解这个建议。为什么我要浪费我的时间在“机械”任务中包装其他精彩的std :: vector类的每个函数。继承确实对这项任务最有意义 - 或者我错过了什么?
答案 0 :(得分:11)
为什么不写一个符合你想要的独立功能:
template<typename T>
void fast_erase(std::vector<T>& v, size_t i)
{
v[i] = std::move(v.back());
v.pop_back();
}
尽管如此,Seth Carnegie的所有功劳。我最初使用“std :: swap”。
答案 1 :(得分:4)
精致的问题。您要破解的第一条准则是:“Inheritance is not for code reuse”。第二个是:“不要继承标准库容器”。
但是:如果你可以保证,没有人会使用你的unordered_vector<T>
vector<T>
你是好的。但是,如果有人这样做,结果可能是不确定的和/或可怕的,无论你有多少成员(它可能看起来很完美,但仍然是未定义的行为!)。
您可以使用private
继承,但这不会让您无法使用 lot 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
。
答案 2 :(得分:3)
继承的风险在以下示例中:
std::vector<something> *v = new better_vector<something>();
delete v;
这会导致问题,因为您删除了指向没有虚析构函数的基类的指针 但是,如果您总是删除指向您的类的指针,如:
better_vector<something> *v = new better_vector<something>();
delete v;
或者不要在堆上分配它没有危险。只是不要忘记在析构函数中调用父析构函数。
答案 3 :(得分:2)
我认为它会将最后一个元素与“待删除”交换掉 元素并将大小减小一个。
vector :: erase维持元素的顺序,同时将最后一个元素移动到已擦除的元素,并将大小减小一个。由于vector实现了数组,因此没有O(1)方法来维护元素的顺序并同时擦除(除非你删除最后一个元素)。
如果维护元素的顺序并不重要,那么你最好使用其他容器(例如 list ,它实现双向链表)。