【问题标题】:Does it make sense to inherit from STL containers and delete `new` operators to prevent undefined behavior because of lacking virtual destructors?从 STL 容器继承并删除 `new` 运算符以防止由于缺少虚拟析构函数而导致的未定义行为是否有意义?
【发布时间】:2014-03-02 04:05:03
【问题描述】:

我在这个论坛上被告知,我在书上看过,还有其他answers说继承STL容器永远不会好,因为STL容器析构函数不是虚拟的(删除时未定义的行为通过基类指针派生的对象)。

但是,C++11 标准现在允许在编译时使用成员函数说明符(如 delete)进行按合同设计的检查。我完全可以理解为什么继承一个 STL 容器的目的是扩展一两个成员函数更好地替换为将成员函数编码为算法。不过,我有一种情况,我将一组元素建模为一个概念,而元素本身就是其他元素的容器。我需要实现访问子元素数据前向迭代器的双向迭代器(例如 Collection::sub_element_iterator)。在这种情况下,使用组合迫使我重新键入(调整)整个 std::vector 公共接口,只是用迭代器扩展这个新 Collection。

如果我仍然使用继承并以下列方式防止堆分配,在这种情况下是否可以?

这是一个小模型:

#include <vector>

class not_heap_allocable
{
    public: 
        void* operator new  (std::size_t count) = delete;
};

template
<
    typename Type,
    template <typename T> class Allocator = std::allocator
>
class extended_vector
:
    public std::vector<Type, Allocator<Type>>, 
    public not_heap_allocable
{
    public: 
        typedef std::vector<Type> BaseType; 

        using BaseType::BaseType; 
};

using namespace std;

int main(int argc, const char *argv[])
{

    vector<int>* vNew = new extended_vector<int> ({0,1,2,3,4,5});  // Compile time error. 

    return 0;
}

这会导致编译时错误:

main.cpp: In function ‘int main(int, const char**)’:
main.cpp:31:64: error: use of deleted function ‘static void* not_heap_allocable::operator new(std::size_t)’
     vector<int>* vNew = new extended_vector<int> ({0,1,2,3,4,5});  // Compile time error. 
                                                                ^
main.cpp:6:15: error: declared here
         void* operator new  (std::size_t count) = delete;

因此,extended_vector 不再指望人类不要滥用它。

【问题讨论】:

  • 我猜你想继承std::vector&lt;Type, Allocator&gt;,而不是std::vector&lt;Type&gt;?
  • @leemes,谢谢,是的!我更正了。
  • 我忍不住,但那味道。
  • 禁止堆分配并不能清楚地表达意图恕我直言(使用非虚拟 dtor,在堆上创建派生类的对象仍然有效,只是不能通过基类指针)。另一种可能性是使用private(或protected)继承并通过using-declarations 重新发布成员函数。
  • not_heap_allocable 也禁止 放置新...还要注意 extended_vector 在堆栈上不是必需的(如果它是其他类的一部分)。跨度>

标签: c++ inheritance stl


【解决方案1】:

这种方法似乎比继承protected 和using(公开)您的子类实际需要公开的基类成员更复杂,并且可能不太清晰。这可以防止所有以这种方式公开继承的问题,同时不会通过引入新的问题而引入任何意外的问题。

编辑:我从 boost 中找到了我想要的东西:这个 SO 问题 Writing an iterator that makes several containers look as one 链接到 boost::join http://www.boost.org/doc/libs/1_46_1/libs/range/doc/html/range/reference/utilities/join.html,它可以让您将多个范围合并为一个。

【讨论】:

  • 你能给我更多关于更复杂和可能不太清楚的细节吗?我的意思是,我理解这里的危险,但是重新引入大部分公共接口不是更复杂吗,因为我希望 Collection 的行为与 Elements 的 vector 完全一样? +1 适配器,如果你找到它,请告诉我 :)
  • 感谢迭代器的链接,但实际上这个答案:stackoverflow.com/a/6748024/735756 更类似于我需要的。集合的集合,而不是两个“相邻”容器。
【解决方案2】:

不,从不是为派生而设计的类派生是没有意义的。

您实际上必须封装类并将每个所需的功能转发给该封装的成员。

由于派生类仍然是基类,一旦它作为基类传递,您将失去派生。

您可以使用私有继承,但仍然必须再次公开每个功能。

【讨论】:

    猜你喜欢
    • 2019-09-08
    • 2016-04-02
    • 1970-01-01
    • 2015-08-22
    • 1970-01-01
    • 2010-12-11
    • 2014-07-30
    • 2011-01-12
    相关资源
    最近更新 更多