在C ++中扩展容器(如std :: vector)的功能的正确方法,而不继承它?

时间:2013-12-14 03:49:27

标签: c++ inheritance stl

我反复阅读继承自STL容器is a Bad Thing (我知道这背后的原因是什么,这不是我的问题。)

牢记以上内容,扩展容器功能的正确方法是什么?

例如,如果我想要一个vector类容器,当operator[]的参数大于或等于容器的大小时自动调整大小,那么我会这样做吗?

对我而言,C ++ 11中最明显的解决方案是:

template<class T, class A = std::allocator<T> >
class auto_vector : public std::vector<T, A>
{
    typedef std::vector<T, A> base_type;
public:
    using base_type::vector;
    typename base_type::reference operator[](typename base_type::size_type n)
    // I don't need const version, so I'll just ignore it here
    {
        if (n >= this->base_type::size())
        { this->base_type::resize(n + 1); }
        return this->base_type::operator[](n);
    }
};

在C ++ 03中,它将是:

template<class T, class A = std::allocator<T> >
class auto_vector : public std::vector<T, A>
{

    typedef std::vector<T, A> base_type;
public:
    explicit auto_vector(
        typename base_type::allocator_type const &alloc =
            typename base_type::allocator_type())
        : base_type(alloc) { }
    explicit auto_vector(
        typename base_type::size_type n,
        value_type const &val = value_type(),
        typename base_type::allocator_type const &alloc =
            typename base_type::allocator_type())
        : base_type(n, val, alloc) { }
    template<class InIt>
    auto_vector(InIt f, InIt l, typename base_type::allocator_type const &alloc =
                                typename base_type::allocator_type())
        : base_type(f, l, alloc) { }
    auto_vector(auto_vector const &v) : base_type(v) { }
    typename base_type::reference operator[](typename base_type::size_type n)
    // I don't need const version
    {
        if (n >= this->base_type::size())
        { this->base_type::resize(n + 1); }
        return this->base_type::operator[](n);
    }
};

这两种都是不好的做法,因为它们继承自std::vector

那么,实现这样的事情的“正确”方法是什么?

2 个答案:

答案 0 :(得分:4)

首先,关于......

  

“我一再认为继承STL容器是一件坏事。”

...使用引用提供STL容器没有虚拟析构函数的原因。

对于新手而言,如果没有虚拟析构函数,那么这对于没有派生出来的课程肯定是个好建议。这使他们无法例如访问std::stack中的受保护成员,它使他们无法使用Microsoft COM技术等,但总而言之,对于新手来说,净优势是巨大的。同样,我们建议初学者不要使用原始数组并指导newdelete,这对于初学者来说可能是一个巨大的净优势,但仍然,一些 - 更有经验 - 必须这样做,有实现新手(理想情况下)约束使用的抽象,以免在任何C ++程序中都没有动态分配。

因此,像std::stack这样的类清楚地证明关于这一点的绝对规则是无意义的,有经验的程序员必须权衡利弊,在决定继承时,必须在public之间进行选择,{ {1}}和protected继承。

Con 公共继承:如果新手动态分配private,然后尝试在指向auto_vector的指针上使用delete来销毁它,那么设计未能指导新手正确使用。因此,如果强烈指导新手纠正使用是一个目标,那么要么不这样做,要么添加使新手动态分配困难的功能。例如。添加std::vector附加不可访问类型的参数,或者本身无法访问。

对于手头的情况,其他多态访问operator new不是问题,因为将std::vector索引超出其大小的代码已经具有未定义的行为。

对于其他情况,可以想象的情况,与OP的情况不同,必须考虑其他情况的细节:一般绝对规则不会在编程中削减它(好吧,至少在C ++编程中没有)。

在这种情况下,

Pro 公共继承:它是“我想要所有基类”功能的最简单,最直接和最明确的表达方式。

答案 1 :(得分:1)

std::vector中缺少虚拟析构函数是一个你不应该掉以轻心的标志。

组合几乎总是优于继承来扩展类型。在你的情况下你应该做的是撰写一个std::vector,并且只重新实现你想要在其上使用的成员函数。最终不应该是一个大问题,因为对于给定的用例,很少使用std::vector的整个公共接口的5-10以上。重新实现的功能只是将他们的调用转发给撰写的std::vector,而您的新operator[]会添加额外的调整大小逻辑。

通过这样做,我们无法像std::vector那样意外地使用您的新类型。