C++ C++;:重载多态单例类上的delete
假设我有以下几点:C++ C++;:重载多态单例类上的delete,c++,memory-management,singleton,operator-overloading,dynamic-memory-allocation,C++,Memory Management,Singleton,Operator Overloading,Dynamic Memory Allocation,假设我有以下几点: struct Base { virtual ~Base() noexcept = default; ... }; struct Singleton : public Base { void* operator new(size_t sz) noexcept { return instance(); } void operator delete(void* ptr) noexcept { // the body is supposed to b
struct Base {
virtual ~Base() noexcept = default;
...
};
struct Singleton
: public Base {
void* operator new(size_t sz) noexcept { return instance(); }
void operator delete(void* ptr) noexcept {
// the body is supposed to be empty since new() doesn't allocate
++delete_count;
}
static Singleton* instance() noexcept {
static Singleton kInstance;
return &kInstance;
}
...
};
(我使用这样的单例类来减少多态容器类中的开销,例如std::vector
,特别是当容器中出现多个时。)
我很惊讶以下代码会崩溃:
有趣的是,如果我使Singleton
类非多态,即从Base
中删除虚拟析构函数,那么一切都可以正常工作(即使我通过添加类成员使Base
类非空)
有人能解释为什么会发生这种情况以及如何解决它吗
编辑:似乎要将单例::删除
更改为:
void operator delete(void* ptr) noexcept {
::new(ptr) Singleton; // re-initialize using placement new
++delete_count;
}
至少在GCC 4.8.1上可以运行,代码在GCC(4.9)和Clang中都运行良好GCC4.8.2失败。VC++2013也失败 这是一本书
您使用的编译器(当然还有版本)是什么?我想这就是g++如何管理其内部状态 我发现这段代码在clang中正确编译和运行,但在gcc中失败。原因是,在第一次删除时,它调用定义的类。但对于第二个电话,它需要全球电话
为了解决这个问题,我建议将分配和解除分配移动到您正在调用的未定义行为的基类。原因如下 除非要删除的指针为null,否则delete表达式始终调用析构函数。引用C++11标准§5.3.5/6 如果删除表达式的操作数值不是空指针值,则删除表达式将 为要删除的对象或数组元素调用析构函数(如果有) 即使delete表达式像这里一样调用用户定义的释放函数,也会发生这种情况 如果对象具有非平凡的析构函数,则对同一对象调用两次析构函数将调用未定义的行为。这要归功于§12.4/15 一旦为对象调用析构函数,该对象就不再存在;如果 为生存期已结束的对象调用析构函数(3.8) 和§3.8 。。。类型为
T
的对象的生存期在以下情况下结束:
-如果T
是具有非平凡析构函数(12.4)的类类型,则析构函数调用将启动,或者
但是,如果基类中的虚拟析构函数被删除,那么
单例
的析构函数将变得微不足道(参见§12.4/5)。在这种情况下,行为根据标准进行了明确定义。(从某种意义上说,这是因为平凡的析构函数不是ops,而非平凡的析构函数实际上修改VPTR等。)您的方法重载new()
和delete
,并声明拥有多个这样的单例是非常奇怪的。可能也是相关的:问题实际上是派生类中重写的操作符delete
是否被父类识别。如果不是,则由于父类Base
调用其默认析构函数的实现,程序具有实现定义的行为(可能是UB,但不确定)。有标准的人应该对此发表评论。VisualStudio2013在deleteS2上逐渐消失
似乎正在调用未被重写的基
析构函数。警告:我必须删除<代码> >除了代码> >代码>默认>代码>指定符,用VS 2013编译。检查GCC 4.82和它失败。GCC 4.8-1(Ubuntu 4.81-2Ubuntu1~ 12.04),你是说这是GCC中的一个bug,还是我的例子是未定义行为(根据C++标准)?我也不太明白为什么只有在类是多态的情况下,seconddelete
才会导致基类的释放
到的主体删除
使行为定义?@leden是的,我认为这使其定义明确。不要引用我的话,但我的补丁看起来不是线程安全的,是吗?例如,考虑使用单线程的线程,而另一个调用“代码>删除< /代码>……@莱登,这是真的。我真的会在这里建议一个不同的设计。我不确定您是否正在使用new
和delete
它们是要使用的。此外,我认为阅读您的代码的人会发现它非常混乱。因为OP使用的是C++11,所以singleton的简单静态变量版本保证是线程安全的。
void operator delete(void* ptr) noexcept {
::new(ptr) Singleton; // re-initialize using placement new
++delete_count;
}