我刚刚回答了a question关于缺少与展示位置相对应的展示位置删除的问题。原因似乎是根据对象的动态类型调用operator delete
的方式(对应于用于查找operator new
的类型)。
展示位置new
对我有用。在自定义分配方面,可能存在一个可重用的类,其中不同的实例管理不同的池。单身人士是一种反模式,所有这一切。
我可以理解在不跟踪分配器的情况下使new thing;
工作的便利性,但是为类型层次结构的不同分支做事似乎相当复杂。
是否存在真实场景,派生类使用与其基础不同的分配器,并依赖虚拟析构函数来查找正确的成员operator delete
?
为了避免这种主观,我会接受最合理的答案。让我们不要对代码气味或“最佳”做事方式嗤之以鼻。
答案 0 :(得分:8)
我过去实际使用过这个!它对非均匀内存架构很有用 - 平台具有非常小的非常快的内存区域而没有操作系统等。
具体而言,设想例如一个带有少量TCM的ARM芯片(紧密耦合的存储器;本质上,嵌入在SoC上的SRAM,例如1个周期的访问时间)。
然后我们在产品开发的最后阶段使用分析器结果 - 就在发货之前(例如,想象一下这是一个流行的手持游戏系统的盒式磁带) - 确定某些类可以从这个更快的SRAM中受益
一个简单的成员operator new
现在可以将这个TCM用于派生类,这可能是有意义的:我们不能使用这个SRAM来获得整个类层次结构,但对于某些低实例化计数但是使用频繁的派生类,它成为一种简单有效的优化。通过这种方式重定向某些分配,我们在少数情况下得到了2%-10%或更多的帧时间。
答案 1 :(得分:5)
我实际上从未在派生类中使用过new / delete重载,或者我曾经想过它,但这个问题很有意思,我决定进行一些研究并试一试。我找到了几个合适的参考文献:
<强> ARM 强> http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka14267.html
这个引用实际上有一个派生类的示例代码,它重载了它的基类重载new。
范德比尔特大学 http://www.vuse.vanderbilt.edu/~adamsja/Courses/CS251/Projects/4/memPool.pdf
此引用没有明确提供有关在派生类中重载new的任何材料,但是,它提到了一些有趣的原因,为什么要重载new。原因包括:
现在基于这两个引用,我已经确定在派生类中重载new / delete可能有一些原因。我的理由基本上与我在VU演示文稿中列出的原因一致,但基于ARM参考暗示我的嵌入式或专用场景也似乎相关。
同样,我还没有找到理由或实际执行过你提到过的事情,但这些是我的想法,只需要一点调查。
答案 2 :(得分:1)
是否存在真实场景,派生类使用与其基础不同的分配器,并依赖虚拟析构函数来查找正确的成员
operator delete
?
我不确定你会考虑什么是真实场景,但我想到的一个显而易见的例子是一个继承层次结构,它根植于一个抽象基类,有很多不同的(大小)派生类,很多其中的一些是定期创建和销毁的,而这些分配/解除分配需要速度。
您可能希望为这些派生类设置自定义分配器,因为特定大小的内存blob的分配器可能非常快,并且您可能希望为每个派生类使用不同的分配器,因为它们的大小非常不同。
但是,我不能给你一个具体的例子,因为多年来我发现避免分配/解除分配的方式比加速它更好,所以近二十年我很少重载特定于类的new
/ delete
。 (当然,正如我们谈论复杂的优化时一样,“游戏”出现了,所以我们可以想象一个游戏需要创建并摧毁大量非常不同的实体。)
答案 3 :(得分:0)
成员operator new
/ delete
可能对从基类中删除不需要的自定义分配很有用,例如,如果不打算扩展基础。但是基础要么需要虚拟析构函数,要么基础对象永远不能成为delete
的主题。
实际上,operator delete
的虚拟析构函数调度与重载无关。可能存在支持多重继承的功能,动态查找正确的成员operator delete
只是副作用。
规则就是最派生对象的虚拟析构函数调用operator delete
。主要原因是传递正确的最大派生指针和大小。上下文还使程序能够动态地找到正确的释放函数,这只是一个快乐的副作用。
因此,动态delete
调度可能有也可能没有用,但绝对没有专门的机制来使其发挥作用。
struct pad {
int x;
virtual ~pad() {}
};
struct b {
int x;
};
struct vb {
int x;
virtual ~vb() {}
};
struct d : pad, b, vb {};
void operator delete( void *p ) {
std::cout << "free " << p << '\n';
}
int main() {
std::cout << "With virtual destructor:\n";
d *p = new d;
std::cout << "allocate " << p << ", delete " << static_cast< vb * >( p ) << '\n';
delete static_cast< vb * >( p );
std::cout << "With plain destructor:\n";
p = new d;
std::cout << "allocate " << p << ", delete " << static_cast< b * >( p ) << '\n';
delete static_cast< b * >( p );
}