好吧,这真的很难承认,但我确实有一个强烈的诱惑,从std::vector继承。

I need about 10 customized algorithms for vector and I want them to be directly members of the vector. But naturally I want also to have the rest of std::vector's interface. Well, my first idea, as a law-abiding citizen, was to have an std::vector member in MyVector class. But then I would have to manually reprovide all of the std::vector's interface. Too much to type. Next, I thought about private inheritance, so that instead of reproviding methods I would write a bunch of using std::vector::member's in the public section. This is tedious too actually.

在这里,我真的认为我可以简单地从std::vector公开继承,但在文档中提供了一个警告,该类不应该多态地使用。我认为大多数开发人员都有足够的能力来理解这无论如何都不应该多态地使用。

我的决定是绝对不合理的吗?如果有,为什么?你能提供一个替代方案,这将有额外的成员实际上成员,但不会涉及重新键入所有矢量的接口?我很怀疑,但如果你能做到,我会很开心的。

而且,除了有些白痴能写出这样的东西

std::vector<int>* p  = new MyVector

使用MyVector还有其他现实的危险吗?通过说现实,我放弃了一些东西,比如想象一个函数,它接受一个指向向量的指针…

我已经陈述了我的观点。我有罪。现在就看你是否原谅我了:)


当前回答

如果你正在考虑这个问题,那么你显然已经把办公室里的语言书呆子都干掉了。有了他们,为什么不直接做呢

struct MyVector
{
   std::vector<Thingy> v;  // public!
   void func1( ... ) ; // and so on
}

这将避免所有可能出现的错误,可能会意外地向上转换你的MyVector类,你仍然可以访问所有的向量操作,只需添加一个小.v。

其他回答

你希望完成什么?只是提供一些功能?

c++惯用的方法是编写一些实现该功能的自由函数。有可能您实际上并不需要std::vector,特别是对于您正在实现的功能,这意味着您实际上通过尝试继承std::vector而失去了可重用性。

我强烈建议您查看标准库和标头,并思考它们是如何工作的。

我认为很少有规则是应该百分之百地盲从的。听起来你已经考虑了很多,并且相信这是一条正确的道路。所以——除非有人提出很好的具体理由不这样做——我认为你应该继续你的计划。

这个问题肯定会让人紧张得喘不过气来,但事实上,没有理由避免从标准容器派生,或者“不必要地增加实体”。最简单、最短的表达是最清晰、最好的。

您确实需要对任何派生类型进行所有通常的注意,但对于来自标准的基类型的情况并没有什么特别之处。重写base成员函数可能很棘手,但对于任何非虚基来说都是不明智的,因此这里没有太多特别之处。如果要添加一个数据成员,如果该成员必须与基库的内容保持一致,则需要考虑切片问题,但这同样适用于任何基库。

The place where I have found deriving from a standard container particularly useful is to add a single constructor that does precisely the initialization needed, with no chance of confusion or hijacking by other constructors. (I'm looking at you, initialization_list constructors!) Then, you can freely use the resulting object, sliced -- pass it by reference to something expecting the base, move from it to an instance of the base, what have you. There are no edge cases to worry about, unless it would bother you to bind a template argument to the derived class.

在c++ 20中,这种技术将立即发挥作用的地方是预留。我们可能在哪里写过

  std::vector<T> names; names.reserve(1000);

我们可以说

  template<typename C> 
  struct reserve_in : C { 
    reserve_in(std::size_t n) { this->reserve(n); }
  };

然后,即使作为班级成员,

  . . .
  reserve_in<std::vector<T>> taken_names{1000};  // 1
  std::vector<T> given_names{reserve_in<std::vector<T>>{1000}}; // 2
  . . .

(根据首选项),而不需要编写构造函数来调用reserve()。

(The reason that reserve_in, technically, needs to wait for C++20 is that prior Standards don't require the capacity of an empty vector to be preserved across moves. That is acknowledged as an oversight, and can reasonably be expected to be fixed as a defect in time for '20. We can also expect the fix to be, effectively, backdated to previous Standards, because all existing implementations actually do preserve capacity across moves; the Standards just haven't required it. The eager can safely jump the gun -- reserving is almost always just an optimization anyway.)

有些人会认为,reserve_in的情况最好由一个免费的函数模板来实现:

  template<typename C> 
  auto reserve_in(std::size_t n) { C c; c.reserve(n); return c; }

这样的替代方案当然是可行的——有时甚至会因为RVO而快得无限大。但是推导或自由函数的选择应该根据其本身的优点,而不是根据对从标准组件推导的毫无根据的迷信。在上面的例子中,只有第二种形式可以使用free函数;尽管在类上下文之外,它可以写得更简洁一点:

  auto given_names{reserve_in<std::vector<T>>(1000)}; // 2

是的,它是安全的,只要你小心不做不安全的事情……我想我从来没有见过有人用向量new,所以在实践中你可能没问题。然而,这并不是c++....中的常用习语

你能提供更多关于算法的信息吗?

有时你在设计上走了一条路,然后看不到你可能走的其他道路——你声称需要用10种新算法来做矢量,这一事实给我敲响了警钟——矢量真的可以实现10种通用算法吗?或者你试图做一个对象,它既是通用矢量,又包含特定的应用程序功能?

我当然不是说你不应该这样做,只是你所提供的信息敲响了警钟,这让我认为也许你的抽象是有问题的,有更好的方法来实现你想要的。

如果你正在考虑这个问题,那么你显然已经把办公室里的语言书呆子都干掉了。有了他们,为什么不直接做呢

struct MyVector
{
   std::vector<Thingy> v;  // public!
   void func1( ... ) ; // and so on
}

这将避免所有可能出现的错误,可能会意外地向上转换你的MyVector类,你仍然可以访问所有的向量操作,只需添加一个小.v。