可以从STL容器inheritance实现,而不是委托吗?

我有一个类,它适应std :: vector模型的特定于域的对象的容器。 我想将大部分std :: vector API暴露给用户,以便他/她可以在容器上使用熟悉的方法(大小,清除,等等)和标准algorithm。 这在我的devise中似乎是一个重复出现的模式:

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) }; 

我意识到在重用类实现时宁愿合成inheritance的做法 – 但是这是有限制的! 如果我把所有东西都委托给std :: vector,那么将会有32个转发函数。

所以我的问题是…在这种情况下inheritance实现真的很糟糕吗? 有什么风险? 有没有更安全的方式,我可以实现这个没有这么多的input? 我是使用实现inheritance的异端吗? 🙂

编辑:

如何清楚地说明用户不应该通过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:

其中一个build议是使用免费function。 我将在这里显示为伪代码:

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

更OO的方式:

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

风险是通过指向基类的指针deletedelete []和潜在的其他释放方法)释放。 由于这些类( dequemapstring等)没有虚拟dtors,所以只有指向这些类的指针才能正确地清理它们:

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

也就是说, 如果你愿意确保你永远不会意外地做到这一点,那么inheritance它们几乎没有什么重大的缺陷 – 但是在某些情况下,这是一个很大的缺陷。 其他缺点包括与实现细节和扩展(其中一些可能不使用保留的标识符)冲突,并处理臃肿的接口(特别是string )。 但是,在某些情况下,inheritance是有意的,因为像堆栈这样的容器适配器有一个受保护的成员c (它们适应的底层容器),并且几乎只能从派生类实例中访问。

可以考虑编写一个自由函数 ,它既可以是一个迭代器对,也可以是一个容器引用,然后对其进行操作。 几乎所有的<algorithm>就是一个例子。 而make_heappop_heappush_heap尤其是使用自由函数而不是特定于域的容器的示例。

因此,为您的数据types使用容器类,并仍然为您的特定于域的逻辑调用免费函数。 但是你仍然可以使用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和allocator可以在不影响后面的代码的情况下使用typedef来更改,甚至容器也可以从一个deque变成一个vector

你可以结合私有inheritance和'using'关键字来解决上面提到的大多数问题:私有inheritance是'is-implemented-in-terms-of'的,因为它是私有的,你不能持有指向基类的指针

 #include <string> #include <iostream> class MyString : private std::string { public: MyString(std::string s) : std::string(s) {} using std::string::size; std::string fooMe(){ return std::string("Foo: ") + *this; } }; int main() { MyString s("Hi"); std::cout << "MyString.size(): " << s.size() << std::endl; std::cout << "MyString.fooMe(): " << s.fooMe() << std::endl; } 

正如大家已经说过的,STL容器没有虚拟析构函数,所以从它们inheritance是不安全的。 我一直认为模板的generics编程是面向对象的一种不同的风格 – 一种没有inheritance的风格。 algorithm定义了他们所需要的接口。 它和Duck Typing一样接近静态语言。

无论如何,我确实有一些要补充的讨论。 我以前创build自己的模板专门化的方式是定义类如下所示的类作为基类。

 template <typename Container> class readonly_container_facade { public: typedef typename Container::size_type size_type; typedef typename Container::const_iterator const_iterator; virtual ~readonly_container_facade() {} inline bool empty() const { return container.empty(); } inline const_iterator begin() const { return container.begin(); } inline const_iterator end() const { return container.end(); } inline size_type size() const { return container.size(); } protected: // hide to force inherited usage only readonly_container_facade() {} protected: // hide assignment by default readonly_container_facade(readonly_container_facade const& other): : container(other.container) {} readonly_container_facade& operator=(readonly_container_facade& other) { container = other.container; return *this; } protected: Container container; }; template <typename Container> class writable_container_facade: public readable_container_facade<Container> { public: typedef typename Container::iterator iterator; writable_container_facade(writable_container_facade& other) readonly_container_facade(other) {} virtual ~writable_container_facade() {} inline iterator begin() { return container.begin(); } inline iterator end() { return container.end(); } writable_container_facade& operator=(writable_container_facade& other) { readable_container_facade<Container>::operator=(other); return *this; } }; 

这些类暴露与STL容器相同的接口。 我喜欢将修改和非修改操作分离到不同的基类中的效果。 这对const正确性有非常好的效果。 一个缺点是,如果你想把这些与关联容器一起使用,你必须扩展接口。 我没有遇到需要。

在这种情况下,inheritance是一个坏主意:STL容器没有虚拟的析构函数,所以你可能会遇到内存泄漏(加上,这表明STL容器并不意味着要被inheritance)。

如果你只需要添加一些function,你可以在全局方法中声明它,或者使用容器成员指针/引用来声明一个轻量类。 这个过程不允许你隐藏方法:如果这真的是你以后的事情,那么没有其他select,然后重新声明整个实现。

抛开虚拟转换器,inheritance和包含的决定应该是基于你正在创build的类的devise决策。 你永远不应该inheritance容器的function,因为它比容纳一个容器更容易,并且添加一些看起来像简单的包装的添加和删除函数, 除非你可以明确地说你正在创build的类是一种容器。 例如,一个课堂课程通常会包含学生对象,但是课堂并不是一个大多数目的的学生列表,所以你不应该从列表中inheritance。

这样做比较容易:

 typedef std::vector<MyObject> MyContainer; 

无论如何,转发方法将被内联。 这样你不会得到更好的performance。 事实上,你可能会变差。