基类'class std :: vector <...>'有一个非虚析构函数

Pyp*_*ros 2 c++ stl vector virtual-destructor

我的一个C++类派生自std::vector它,它可以充当一个容器,也可以对其内容执行自定义操作.不幸的是,编译器抱怨析构函数不是虚拟的,我无法改变,因为它在标准库中.

我做错了什么(你不应该从STL派生)或者我能做些什么来保持编译器的快乐?(appart从停止使用-Weffc ++ :)

编辑:派生类不接触矢量操作算法,而只是为图像矢量添加一些信息,如"元素宽度/高度".举个例子,你可以想到

class PhotoAlbum: public std::vector<Photo> {
    String title;
    Date from_time, to_time;
    // accessors for title and dates
    void renderCover(Drawable &surface);
};
Run Code Online (Sandbox Code Playgroud)

您认为相册主要是带有一些元数据(标题和时间)和专辑特定功能的图片集合,例如将某些照片的缩略图渲染到曲面上以制作专辑封面.所以imho,相册IS-A的集合Photo,比它更多的HAS-A这样的收藏.

我没有看到在获得额外"收集"字段的getPhotoVector()方法中获得任何好处PhotoAlbum.

In *_*ico 16

为什么不使用作文?只需创建std::vector自定义容器的成员,然后将自定义操作实现为作用于该std::vector成员的所述类的成员函数.这样,您就可以完全控制它.此外,如果不需要继承,您应该更喜欢组合而不是继承.


Ste*_*sop 10

拥有一个非虚拟析构函数的公共基类是安全的,但是如果有人为你的类分配一个实例,用new它引用它vector<...>*,然后使用该指针删除它而不将其强制转换为指针,则行为是未定义的到你的班级.因此,您班级的用户必须知道不要这样做.阻止它们的最可靠方法是不给它们机会,因此编译器警告.

要处理这个问题而不必对用户施加这样的奇怪条件,最好的建议是对于C++中的公共基类,析构函数应该是公共的和虚拟的,或者是受保护的和非虚拟的(http:// www. gotw.ca/publications/mill18.htm,准则#4).由于析构函数std::vector不是,这意味着它不应该用作公共基类.

如果你想要的只是在向量上定义一些额外的操作,那么这就是C++中的自由函数..无论如何,成员调用语法有什么好处?大多数<algorithm>包括对矢量和其他容器的附加操作.

例如,如果你想创建一个"带有最大大小限制的向量",这将提供vector带有修改语义的整个接口,那么与继承和虚拟调用是常态的语言相比,实际上C++确实使这有点不方便.最简单的方法是使用私有继承,然后对于vector您不想更改的成员函数,将它们带入您的类using:

#include <vector>
#include <iostream>
#include <stdexcept>

class myvec : private std::vector<int> {
    size_t max_size;
  public:
    myvec(size_t m) : max_size(m) {}
    // ... other constructors

    void push_back(int i) {
        check(size()+1);
        std::vector<int>::push_back(i);
    }
    // ... other modified functions

    using std::vector<int>::operator[];
    // ... other unmodified functions

  private:
    void check(size_t newsize) {
        if (newsize > max_size) throw std::runtime_error("limit exceeded");
    }
};

int main() {
    myvec m(1);
    m.push_back(3);
    std::cout << m[0] << "\n";
    m.push_back(3); // throws an exception
}
Run Code Online (Sandbox Code Playgroud)

不过,你仍然需要小心.C++标准不保证vector相互调用哪些函数,或以何种方式调用.在那些调用确实发生的地方,我的vector基类无法调用重载myvec,所以我改变的函数根本不适用 - 这对你来说是非虚函数.我不能只超载resize()myvec,并用它做,我必须每天超负荷改变大小的功能,使他们所有的呼叫check(直接或通过调用对方).

你可以从标准中的限制中推断出有些事情是不可能的:例如,operator[]不能改变向量的大小,所以在我的例子中我可以安全地使用基类实现,而我只需要重载函数这可能会改变大小.但是,该标准不一定能为所有可想到的派生类提供这种保证.

简而言之,std::vector不是设计为基类,因此它可能不是一个表现良好的基类.

当然,如果使用私有继承,则无法传递myvec给需要向量的函数.但那是因为它不是一个向量 - 它的push_back函数甚至没有与向量相同的语义,所以我们使用LSP进行狡猾的讨论,但更重要的是对vector忽略我们的重载的函数进行非虚拟调用.如果按照标准库预期的方式执行操作,那就没关系 - 使用大量模板,并传递迭代器而不是集合.如果你想要虚函数调用,那就不行了,因为除了vector没有虚拟析构函数之外,它没有任何虚函数.

如果你真的想要使用标准容器进行动态多态(也就是说,你想做 vector<int> *ptr = new myvec(1);),那么你就进入了"你不应该"的领域.标准库无法真正帮助您.