Do I need virtual destructors in a world without dynamic memory?
当对象(可能)被基类指针破坏时,需要使用虚拟析构函数。
考虑一个没有动态内存的程序,就像在嵌入式系统中常见的那样。这里,使用new或delete会触发一个链接器错误,因为所需的底层分配器没有实现。因此,开发人员只使用静态分配的对象(在bss/data部分)或自动分配的对象(通常在堆栈上)。
在这样的系统中,是否存在真正需要虚拟析构函数的情况?(假设没有人感到无聊,并在某个指针上手动调用析构函数。)
在我看来,静态和自动分配总是调用正确的析构函数。我有错过什么吗?有角箱吗?静态对象池与唯一的指针和自定义的删除器结合在一起怎么样?
- 不,你什么都没漏掉。只有通过基类指针调用派生析构函数时才需要虚拟析构函数。否则,基析构函数不需要是虚拟的。
- 当然,把所有你没有设计成多态的类都变成final。你未来的自我会感谢你。
- 可能对释放内存以外的资源有一定的用处。使用自定义位置新建/删除也很常见。目前,模板对于多态性工作通常更好。
- "(假设没有人感到无聊,在某个指针上手动调用析构函数。"—这是什么意思?
- 在Doug所说的基础上,静态多态性模板(即编译时的多态性)可以完成动态多态性被过度使用的大部分工作。
- @NeilButterworth意味着没有人将指针强制转换为堆栈分配对象的基类,并手动调用base->~Base()。
- @埃尔杰:"当然,让你所有的类,你不打算是多态的,成为最终的。你未来的自我会感谢你的。"不,你未来的自我会恨你,当他们作出一个分配器类型final,并发现标准库类型想要从它继承(由于ebo的原因),但不能,从而打破。默认情况下,一类EDCOX1(0)是非常粗鲁的C++语言。我们使用继承不仅仅是多态性。
- 尼可波拉斯?不是我的经验。没有被设计成多态使用的类会导致许多问题。为什么有人会期望从一个我无法理解的非多态类电子书。
- @Eljay:"为什么有人会期望EBO来自一个我无法理解的非多态类。"想想std::vector>。分配器没有成员,因此不需要占用空间。但是如果你使它成为vector的成员子对象,它必须占据空间。因此,它继承了allocator;基类不必占用派生类中的空间。这使得vector更小;它只占用3个空间指针。对于这种低级服务代码,这是一个相当常见的工具。
- @埃尔杰:没关系。但我从C++标准库的每一个实现中都给出了一个例子。对于他们所使用的语言,标准库甚至不能使用的建议可能不是最好的建议。我并不是说你的用例是错误的或者什么。只是,如果您的建议在应用于标准库时被破坏,那么您的视角可能不够宽。
- 尼可波拉斯?EBO不适用于大多数非多态类。
- @埃杰伊:电子书是一种类型的用户的问题。在vector的情况下,EBO是由vector的实施引起的,而不是由allocator类引起的。空的allocator类只能够有效地利用vector。无论您认为是否适用,final分配器都可能破坏标准库容器。
- 尼可波拉斯?好吧,很长时间内我不需要做一个分配器。我很抱歉。不要使分配器成为最终的。对于非分配器、非多态类,使它们成为最终类。
- @埃尔杰:当然,除非有人想在某种类型的混音中使用您的类型,这需要从中继承。由于mixin的要点是,类的编写者实际上不知道用户是否希望在该mixin中使用它,因此您无法确定您的类型是否将以这种方式使用。在C++中有很多其他的习惯用法打破了类EDCOX1的0。类EDOCX1 0是不是你不应该在C++中使用的东西。
- 型@尼古拉斯?类final是一个应该深思熟虑地省略的东西。对于没有数据成员的mixin,它应该被省略,因为mixin被设计成多态使用。对于设计为多态基类的类,应该省略它。如果一个类需要成为一个多态的基,那么它应该被删除,并且在那时,该类被重新构造为适合作为一个多态的基类。虽然太晚了,C++应该有EDOCX1,0作为动态多态性的默认和选择。
Let's say nobody is bored and calls a destructor manually on some pointer.
我认为你很快就排除了这种可能性。禁止动态分配的嵌入式/内存受限系统仍然可以创建具有动态存储持续时间的对象。观察:
1 2 3 4 5 6
| alignas(T) char memory[sizeof(T)];
T *p = new(memory) T; //Does not call global `new` allocator.
/*do stuff with `p`*/
p->~T(); |
没有理由禁止这样做。事实上,一些类型擦除的实现依赖于这一点和小对象优化。小对象的std::any实现可以完全使用std::any对象本身的内存来构造派生类。但它仍然需要调用类型的析构函数,通常是通过基类指针。当然,还有一些any的实现不使用继承,但我的总体观点是,明确禁止手动调用析构函数会很奇怪。