从 STL 容器继承实现而不是委托可以吗? [英] Is it okay to inherit implementation from STL containers, rather than delegate?

查看:36
本文介绍了从 STL 容器继承实现而不是委托可以吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a class that adapts std::vector to model a container of domain-specific objects. I want to expose most of the std::vector API to the user, so that they may use familiar methods (size, clear, at, etc...) and standard algorithms on the container. This seems to be a reoccurring pattern for me in my designs:

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 个转发函数!

I'm aware of the practice of preferring composition to inheritance when reusing a class for implementation -- but there's gotta be a limit! If I were to delegate everything to std::vector, there would be (by my count) 32 forwarding functions!

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

So my questions are... Is it really so bad to inherit implementation in such cases? What are the risks? Is there a safer way I can implement this without so much typing? Am I a heretic for using implementation inheritance? :)

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

What about making it clear that the user should not use MyContainer via a std::vector<> pointer:

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

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

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

The boost libraries seem to do this stuff all the time.

编辑 2:

建议之一是使用免费功能.我将在此处显示为伪代码:

One of the suggestions was to use free functions. I'll show it here as pseudo-code:

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

一种更面向对象的方式:

A more OO way of doing it:

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

推荐答案

风险是通过指向基类的指针来解除分配 (delete, delete[],以及可能的其他解除分配方法).由于这些类(dequemapstring 等)没有虚拟 dtor,因此不可能仅使用指向这些类的指针:

The risk is deallocating through a pointer to the base class (delete, delete[], and potentially other deallocation methods). Since these classes (deque, map, string, etc.) don't have virtual dtors, it's impossible to clean them up properly with only a pointer to those classes:

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

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

That said, if you're willing to make sure you never accidentally do this, there's little major drawback to inheriting them—but in some cases that's a big if. Other drawbacks include clashing with implementation specifics and extensions (some of which may not use reserved identifiers) and dealing with bloated interfaces (string in particular). However, inheritance is intended in some cases, as container adapters like stack have a protected member c (the underlying container they adapt), and it's almost only accessible from a derived class instance.

不要使用继承或组合,考虑编写自由函数,这些函数接受迭代器对或容器引用,并对其进行操作.几乎所有的<算法>就是一个例子;和 make_heappop_heappush_heap 是使用自由函数而不是特定于域的容器的示例.

Instead of either inheritance or composition, consider writing free functions which take either an iterator pair or a container reference, and operate on that. Practically all of <algorithm> is an example of this; and make_heap, pop_heap, and push_heap, in particular, are an example of using free functions instead of a domain-specific container.

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

So, use the container classes for your data types, and still call the free functions for your domain-specific logic. But you can still achieve some modularity using a typedef, which allows you to both simplify declaring them and provides a single point if part of them needs to change:

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.

Notice the value_type and allocator can change without affecting later code using the typedef, and even the container can change from a deque to a vector.

这篇关于从 STL 容器继承实现而不是委托可以吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆