从 STL 容器继承实现而不是委托可以吗?

新手上路,请多包涵

我有一个类可以适应 std::vector 来对特定于域的对象的容器进行建模。我想向用户公开大部分 std::vector API,以便他们可以在容器上使用熟悉的方法(大小、清除、at 等)和标准算法。这似乎是我设计中反复出现的模式:

 class MyContainer : public std::vector<MyObject>
{
public:
   // Redeclare all container traits: value_type, iterator, etc...

   // Domain-specific constructors
   // (more useful to the user than std::vector ones...)

   // Add a few domain-specific helper methods...

   // Perhaps modify or hide a few methods (domain-related)
};

我知道在重用类进行实现时更喜欢组合而不是继承的做法——但必须有一个限制!如果我将所有内容都委托给 std::vector,那么(据我计算)将有 32 个转发函数!

所以我的问题是……在这种情况下继承实现真的那么糟糕吗?有哪些风险?有没有一种更安全的方法可以在没有太多输入的情况下实现它?我是使用实现继承的异端吗? :)

编辑:

如何明确说明用户不应通过 std::vector<> 指针使用 MyContainer:

 // non_api_header_file.h
namespace detail
{
   typedef std::vector<MyObject> MyObjectBase;
}

// api_header_file.h
class MyContainer : public detail::MyObjectBase
{
   // ...
};

boost 库似乎一直在做这些事情。

编辑2:

其中一项建议是使用免费功能。我将在此处将其显示为伪代码:

 typedef std::vector<MyObject> MyCollection;
void specialCollectionInitializer(MyCollection& c, arguments...);
result specialCollectionFunction(const MyCollection& c);
etc...

一种更面向对象的方式:

 typedef std::vector<MyObject> MyCollection;
class MyCollectionWrapper
{
public:
   // Constructor
   MyCollectionWrapper(arguments...) {construct coll_}

   // Access collection directly
   MyCollection& collection() {return coll_;}
   const MyCollection& collection() const {return coll_;}

   // Special domain-related methods
   result mySpecialMethod(arguments...);

private:
   MyCollection coll_;
   // Other domain-specific member variables used
   // in conjunction with the collection.
}

原文由 Emile Cormier 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 517
1 个回答

风险在于通过指向基类的指针deletedelete[] 以及可能的其他释放方法)进行释放。由于这些类( dequemapstring 等)没有虚拟 dtor,因此仅使用指向这些类的指针是不可能正确清理它们的:

 struct BadExample : vector<int> {};
int main() {
  vector<int>* p = new BadExample();
  delete p; // this is Undefined Behavior
  return 0;
}

也就是说, 如果 您愿意确保自己不会意外地这样做,那么继承它们几乎没有什么重大缺点——但在某些情况下,这是一个很大的假设。其他缺点包括与实现细节和扩展发生冲突(其中一些可能不使用保留标识符)以及处理臃肿的接口(特别是 _字符串_)。然而,在某些情况下,继承是有意的,因为像 stack 这样的容器适配器有一个受保护的成员 c (它们适应的底层容器),并且它几乎只能从派生类实例中访问。

考虑编写自由函数,而不是继承或组合,它采用迭代器对或容器引用,并对其进行操作。几乎所有的 都是一个例子;特别是 _makeheap 、 _popheap 和 _pushheap 是使用自由函数而不是特定于域的容器的示例。

因此,为您的数据类型使用容器类,并且仍然为您的域特定逻辑调用自由函数。但是您仍然可以使用 typedef 实现一些模块化,这允许您简化声明它们并在它们的一部分需要更改时提供单点:

 typedef std::deque<int, MyAllocator> Example;
// ...
Example c (42);
example_algorithm(c);
example_algorithm2(c.begin() + 5, c.end() - 5);
Example::iterator i; // nested types are especially easier

请注意 value_type 和分配器可以更改而不会影响使用 typedef 的后续代码,甚至容器也可以从 deque 更改为 vector

原文由 Roger Pate 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题