代码之家  ›  专栏  ›  技术社区  ›  Mr Fooz

为什么STL容器没有虚拟析构函数?

  •  31
  • Mr Fooz  · 技术社区  · 15 年前

    有人知道为什么STL容器没有虚拟析构函数吗?

    据我所知,唯一的好处是:

    • 它将实例的大小减少一个指针(指向虚拟方法表)并
    • 它使破坏和建设稍微快一点。

    缺点是以通常的方式对容器进行子类化是不安全的。

    编辑: 也许我的问题可以重新表述为“为什么STL容器的设计不允许继承?”

    因为它们不支持继承,当一个人想要拥有一个需要STL功能和少量附加功能的新容器时(比如一个专门的构造函数或一个映射的默认值的新访问器,或者其他什么),他就不得不选择以下选项:

    • 组合和接口复制 :创建一个新模板或类,该模板或类作为私有成员拥有STL容器,并且每个STL方法都有一个传递内联方法。这与继承的性能一样,避免了虚拟方法表的成本(在重要的情况下)。不幸的是,STL容器有相当广泛的接口,因此这需要许多行代码来完成一些看似容易的事情。
    • 只做函数 :使用裸(可能是模板)文件作用域函数,而不是尝试添加成员函数。在某些方面,这可能是一种很好的方法,但是封装的好处已经失去了。
    • 具有公共STL访问的合成 :让STL容器的所有者允许用户访问STL容器本身(可能通过访问者进行保护)。对于库编写器来说,这需要最少的编码,但对用户来说却不太方便。组合的一大卖点是减少代码中的耦合,但此解决方案将STL容器与所有者容器完全耦合(因为所有者返回真正的STL容器)。
    • 编译时多态性

    编辑2:

    @doc points out C++ 11的爱好者 using 声明在某种程度上降低了合成的成本。

    9 回复  |  直到 7 年前
        1
  •  29
  •   JaredPar    15 年前

    虚拟析构函数仅对继承场景有用。STL容器的设计目的不是从中继承(也不是受支持的方案)。因此,它们没有虚拟析构函数。

        2
  •  18
  •   iceiceice    4 年前

    我认为斯特劳斯特鲁普在他的精彩论文中间接地回答了这个问题: Why C++ is not just an ObjectOriented Programming Language :

    7闭幕词
    上述设施 是否面向对象?哪一个? 面向对象?在大多数情况下,我 我认为这些问题是错误的。 重要的是你能想出什么主意 从不同的角度组合软件 结果程序的可维护性 是换句话说,你是如何支持的 良好的编程技巧和良好的编程能力 设计技术比设计更重要 通过抽象编程。你 要隐藏详细信息,请执行以下操作: 利用系统中的任何共性, 你想让这个价格合理。 我想鼓励你不要这样做 学期面向对象的概念

    把它等同于好,,

    等同 它使用单一语言,或

    通过 接受一切 面向对象的。

    我认为 有而且一定有用 超越面向对象的技术 编程和设计。但是, 我希望避免被完全误解 我想强调的是,我 我不会尝试一个严肃的项目 使用一种编程语言 至少我不支持古典音乐 面向对象编程的概念。 除了支持 除了那些支持 概念和概念的直接表达 关系。

    通用编程+函数样式+数据抽象==STL样式 . 这并不奇怪,OOP是最重要的,而不是最重要的 表示数据结构的方法&算法库。虽然OOP用于标准库的其他部分,但STL的设计者发现,上述三种技术的混合比OOP更好 单独地 . 简而言之,这个库不是用OOP设计的,在C++中,如果你不使用它,它就不会与你的代码捆绑在一起。你不用的东西你不用付钱。类std::vector、std::list、,。。。是 抽象数据类型 以最好的解释。

        3
  •  14
  •   Jim Buck    15 年前

    我想这是遵循C++哲学的,你不为不使用的功能付费。根据平台的不同,如果您不关心虚拟析构函数,那么虚拟表指针可能是一个巨大的代价。

        4
  •  8
  •   mip    4 年前

    为什么STL容器的设计不允许继承?

    依我拙见,他们是。如果他们不愿意,他们已经被制造出来了 . 当我看到 stl_vector.h 我可以看到我的STL实现使用的源代码 受保护的 _Vector_base<_Tp, _Alloc>

     template<typename _Tp, typename _Alloc = allocator<_Tp> >
     class vector : protected _Vector_base<_Tp, _Alloc>
    

    它不会用吗 私有的 如果子类化不受欢迎,是否继承?


    为什么不使用 protected private 继承并公开接口的所需部分 using 关键词?

    class MyVector : private std::vector<int>
    {
         typedef std::vector<int> Parent;
    
         public:
            using Parent::size;
            using Parent::push_back;
            using Parent::clear;
            //and so on + of course required ctors, dtors and operators.
    };
    

    这种方法确保类的用户不会将实例向上转换为 std::vector<int> 他是安全的,因为非虚拟析构函数的唯一问题是,当对象作为父类的实例被删除时,它不会调用派生析构函数。

    …我也有一个松散的想法,如果你的类没有析构函数,你甚至可以公开继承。异端

        5
  •  1
  •   zumalifeguard Arctic    15 年前

        6
  •  1
  •   Al Conrad    8 年前

    另一个能够从STL容器生成子类的解决方案是Bo-Qian使用智能指针给出的解决方案。

    Advanced C++: Virtual Destructor and Smart Destructor

    class Dog {
    public:
       ~Dog() {cout << "Dog is destroyed"; }
    };
    
    class Yellowdog : public Dog {
    public:
       ~Yellowdog() {cout << "Yellow dog destroyed." << endl; }
    };
    
    
    class DogFactory {
    public:
       static shared_ptr<Dog> createYellowDog() { 
          return shared_ptr<Yellowdog>(new Yellowdog()); 
       }    
    };
    
    int main() {
        shared_ptr<Dog> pd = DogFactory::createYellowDog();
    
        return 0;
    }
    

    这避免了使用虚拟析构函数的难题。

        7
  •  0
  •   ScaryAardvark    15 年前

    正如已经指出的,STL容器的设计不是为了可继承。没有虚拟方法,所有数据成员都是私有的,没有受保护的getter/setter/helper。。正如你所发现的,没有虚拟析构函数。。

        8
  •  -2
  •   psychotik    15 年前

    没有虚拟析构函数阻止类正确地成为子类。

        9
  •  -2
  •   Daniel Frużyński    9 年前

    如果您确实需要虚拟析构函数,可以将其添加到从vector派生的类中<>,然后在任何需要虚拟接口的地方使用这个类作为基类。通过这样做,编译器将从基类调用虚拟析构函数,而基类又将从向量类调用非虚拟析构函数。

    例子:

    #include <vector>
    #include <iostream>
    
    using namespace std;
    
    class Test
    {
        int val;
    public:
        Test(int val) : val(val)
        {
            cout << "Creating Test " << val << endl;
        }
        Test(const Test& other) : val(other.val)
        {
            cout << "Creating copy of Test " << val << endl;
        }
        ~Test()
        {
            cout << "Destructing Test " << val << endl;
        }
    };
    
    class BaseVector : public vector<Test>
    {
    public:
        BaseVector()
        {
            cout << "Creating BaseVector" << endl;
        }
        virtual ~BaseVector()
        {
            cout << "Destructing BaseVector" << endl;
        }
    };
    
    class FooVector : public BaseVector
    {
    public:
        FooVector()
        {
            cout << "Creating FooVector" << endl;
        }
        virtual ~FooVector()
        {
            cout << "Destructing FooVector" << endl;
        }
    };
    
    int main()
    {
        BaseVector* ptr = new FooVector();
        ptr->push_back(Test(1));
        delete ptr;
    
        return 0;
    }
    

    此代码提供以下输出:

    Creating BaseVector
    Creating FooVector
    Creating Test 1
    Creating copy of Test 1
    Destructing Test 1
    Destructing FooVector
    Destructing BaseVector
    Destructing Test 1