好吧,这真的很难承认,但我确实有一个强烈的诱惑,从std::vector继承。

I need about 10 customized algorithms for vector and I want them to be directly members of the vector. But naturally I want also to have the rest of std::vector's interface. Well, my first idea, as a law-abiding citizen, was to have an std::vector member in MyVector class. But then I would have to manually reprovide all of the std::vector's interface. Too much to type. Next, I thought about private inheritance, so that instead of reproviding methods I would write a bunch of using std::vector::member's in the public section. This is tedious too actually.

在这里,我真的认为我可以简单地从std::vector公开继承,但在文档中提供了一个警告,该类不应该多态地使用。我认为大多数开发人员都有足够的能力来理解这无论如何都不应该多态地使用。

我的决定是绝对不合理的吗?如果有,为什么?你能提供一个替代方案,这将有额外的成员实际上成员,但不会涉及重新键入所有矢量的接口?我很怀疑,但如果你能做到,我会很开心的。

而且,除了有些白痴能写出这样的东西

std::vector<int>* p  = new MyVector

使用MyVector还有其他现实的危险吗?通过说现实,我放弃了一些东西,比如想象一个函数,它接受一个指向向量的指针…

我已经陈述了我的观点。我有罪。现在就看你是否原谅我了:)


当前回答

我认为很少有规则是应该百分之百地盲从的。听起来你已经考虑了很多,并且相信这是一条正确的道路。所以——除非有人提出很好的具体理由不这样做——我认为你应该继续你的计划。

其他回答

整个STL被设计成算法和容器是分开的。

这就产生了不同类型迭代器的概念:const迭代器、随机访问迭代器等等。

因此,我建议你接受这个约定,并以这样的方式设计你的算法,即它们不会关心它们正在处理的容器是什么——它们只需要特定类型的迭代器来执行它们的操作。

另外,让我把你引向杰夫·阿特伍德的一些精彩评论。

是的,它是安全的,只要你小心不做不安全的事情……我想我从来没有见过有人用向量new,所以在实践中你可能没问题。然而,这并不是c++....中的常用习语

你能提供更多关于算法的信息吗?

有时你在设计上走了一条路,然后看不到你可能走的其他道路——你声称需要用10种新算法来做矢量,这一事实给我敲响了警钟——矢量真的可以实现10种通用算法吗?或者你试图做一个对象,它既是通用矢量,又包含特定的应用程序功能?

我当然不是说你不应该这样做,只是你所提供的信息敲响了警钟,这让我认为也许你的抽象是有问题的,有更好的方法来实现你想要的。

如果你遵循好的c++风格,缺少虚函数不是问题,而是切片(参见https://stackoverflow.com/a/14461532/877329)

为什么虚函数的缺失不是问题?因为函数不应该尝试删除它接收到的任何指针,因为它没有指针的所有权。因此,如果遵循严格的所有权策略,就不应该需要虚拟析构函数。例如,这总是错误的(有或没有虚析构函数):

void foo(SomeType* obj)
    {
    if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
        {
        obj->doStuff();
        }
    delete obj;
    }

class SpecialSomeType:public SomeType
    {
    // whatever 
    };

int main()
    {
    SpecialSomeType obj;
    doStuff(&obj); //Will crash here. But caller does not know that
//  ...
    }

相反,这将始终工作(使用或不使用虚析构函数):

void foo(SomeType* obj)
    {
    if(obj!=nullptr) //The function prototype only makes sense if parameter is optional
        {
        obj->doStuff();
        }
    }

class SpecialSomeType:public SomeType
    {
    // whatever 
    };

int main()
    {
    SpecialSomeType obj;
    doStuff(&obj);
//  The correct destructor *will* be called here.
    }

如果该对象是由工厂创建的,工厂还应该返回一个指向工作删除器的指针,应该使用该指针而不是delete,因为工厂可能使用自己的堆。调用者可以以share_ptr或unique_ptr的形式获取它。简而言之,不要删除不是直接从new中获取的任何内容。

我最近也继承了std::vector,发现它非常有用,到目前为止我还没有遇到任何问题。

我的类是一个稀疏矩阵类,这意味着我需要在某个地方存储我的矩阵元素,即在std::vector中。我继承的原因是我有点太懒了,不愿意为所有的方法编写接口,而且我正在通过SWIG将类接口到Python,其中已经有std::vector的良好接口代码。我发现将这个接口代码扩展到我的类中要比从头开始编写一个新代码容易得多。

这种方法的唯一问题不是非虚析构函数,而是一些我想重载的其他方法,比如push_back()、resize()、insert()等。私人继承的确是一个不错的选择。

谢谢!

实际上:如果派生类中没有任何数据成员,就不会有任何问题,甚至在多态使用中也不会。只有在基类和派生类的大小不同和/或有虚函数(即v-table)时,才需要虚析构函数。

但在理论上:来自c++ 0x FCD中的[expr.delete]:在第一个替代方案(删除对象)中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应是要删除的对象的动态类型的基类,并且静态类型应具有虚析构函数或行为未定义。

但是你可以从std::vector中私有地派生而没有问题。 我使用了以下模式:

class PointVector : private std::vector<PointType>
{
    typedef std::vector<PointType> Vector;
    ...
    using Vector::at;
    using Vector::clear;
    using Vector::iterator;
    using Vector::const_iterator;
    using Vector::begin;
    using Vector::end;
    using Vector::cbegin;
    using Vector::cend;
    using Vector::crbegin;
    using Vector::crend;
    using Vector::empty;
    using Vector::size;
    using Vector::reserve;
    using Vector::operator[];
    using Vector::assign;
    using Vector::insert;
    using Vector::erase;
    using Vector::front;
    using Vector::back;
    using Vector::push_back;
    using Vector::pop_back;
    using Vector::resize;
    ...