2012-08-13 58 views
2

所以我读了从STL继承是一个坏主意。但是如何将STL类包装到其他类中以扩展它们。这与主要目的是分离抽象层次等等。包装STL为了延伸

喜欢的东西:

template<class T> 
class MyVector{ 
public: 
T& last(){ 
    return data[data.size() - 1] 
} 

bool include(T& element); 

//etc. 

private: 
vector<T> data; 
} 

这是一个好主意?这是C++程序员经常做的事吗?

谢谢。

+6

'std :: vector :: back()'不,这不是首选的方法。首选的方法是将底层容器抽象出来并在迭代器/整个范围上操作,或者编写一个超类函数'bool include(std :: vector const&v,T const&elem)''。 – Xeo 2012-08-13 20:20:35

+1

他们不经常这样做,因为大部分时间,只要在适当的容器上使用适当的算法就足够了。 – Griwes 2012-08-13 20:22:02

回答

3

是的,包装比继承好,但只有当你需要添加状态到现有的数据结构。否则,添加非成员函数。这更详细地在C项35说明++编码标准(Amazon

要添加状态,宁可组合物,而不是继承。无可否认,必须为要保留的成员函数 编写直通功能,但这种实现比使用公共或非公共继承的 更安全,更安全。

template<typename T> 
class MyExtendedVector 
{ 
public: 
    // pass-through functions for std::vector members 
    void some_existing_fun() { return vec_.some_existing_fun(); } 

    // new functionality that uses extra state 
    void some_new_fun() { // your implementation here } 

private: 
    std::vector<T> vec_; 
    // extra state e.g. to do logging, caching, or whatever 
}; 

要添加行为,宁愿加NONMEM-BER函数而不是成员 功能。

但请务必让你的算法尽可能通用:

// if you CAN: write algorithm that uses iterators only 
// (and delegates to iterator category that container supports) 
template<typename Iterator, typename T> 
bool generic_contains(Iterator first, Iterator last, T const& elem) 
{ 
    // implement using iterators + STL algorithms only 
    return std::find(first, last, elem) != last; 
} 

// if you MUST: write algorithm that uses specific interface of container 
template<typename T> 
void vector_contains(std::vector<T> const& v, T const& elem) 
{ 
    // implement using public interface of Container + STL algorithms 
    return generic_contains(v.begin(), v.end(), elem); 
} 
3

我可以为我只能说,但我没有这样做,我可能就不是一般的建议吧。

在几乎所有情况下,基于迭代器的算法实现起来都比较简单将算法从容器中分离出来。例如,假设您的include方法仅仅是确定元素是否在矢量中,则根据容器的内容和搜索需要,您可以使用find,binary_searchlower_bound

偶尔我实现一类通过提供开始/结束方法看起来像对外界的容器。在这种情况下,它有时会有一个标准的容器底层,但是你只实现了一个最小化的公共接口来表示你的类实际建模的内容。

2

我会避免这样做,因为最后一个人回答。我明白你要去哪里你的容器要将一些更复杂的操作包装成更简单的方法,并且认为你可以在某些时候更改底层容器而不必更改接口。然而,话虽如此,你的对象应该模拟你的业务需求,然后这些对象的实现将使用任何数据容器和访问模式是最好的。我想我所说的是,你最终不会重新使用这个新的向量类,因为每次你的业务需求的数据访问都会不同,你会再次使用一些标准的通用容器,比如std :: vector,基于迭代器的算法访问数据。

现在,如果有一些不存在的算法,您可以为该特定项目编写基于迭代器的算法,然后保留可能能够重用的算法代码。下面显示了我写的一组grep算法,它基于一组交集,但没有按照我想要的完成。我可以再次使用这个算法。

#include <utility> 
#include <algorithm> 

// this is a set grep meaning any items that are in set one 
// will be pulled out if they match anything in set 2 based on operator pred 
template<typename _InputIterator1, typename _InputIterator2, 
    typename _OutputIterator, typename _Compare> 
    _OutputIterator 
setGrep(_InputIterator1 __first1, _InputIterator1 __last1, 
    _InputIterator2 __first2, _InputIterator2 __last2, 
    _OutputIterator __result, _Compare __comp) 
{ 
    while (__first1 != __last1 && __first2 != __last2) 
    if (__comp(*__first1, *__first2)) 
     ++__first1; 
    else if (__comp(*__first2, *__first1)) 
     ++__first2; 
    else 
    { 
     *__result = *__first1; 
     ++__first1; 
     ++__result; 
    } 
    return __result; 
} 
+0

标准库实现中使用的命名约定并不意味着要模拟。包含双下划线和以下划线开头并带有大写字母的名称的名称为实现保留,不会在用户代码中出现。 – 2012-08-13 21:38:19