2014-10-04 46 views
2

我使用的框架,它有一个类注册,我能注册重载delete运算符来删除库

Register r; 
A * a1 = new A(); 
r->register(a1); 
A * a2 = new A(); 
r->register(a2); 

注册将采取所有权A的情况下,在其中,并删除所有已注册分配的东西A当超出范围。

我要修改共享库(。所以在我的情况)A的行为,所以,我会做这样的事情(库):

class B : public A { 
    ... 
} 

B * get_customized_a() { 
    return new B(); 
} 

然后在主程序

Register r; 
A * a1 = get_customized_a(); 
r->register(a1); 

但现在a1将在主程序中被删除,而不是在图书馆!我认为这是一个很大的禁忌。

那么如何解决这个问题?


我提出了两个解决方案:

1)使用和插件通过独立的功能

定制:

void customize_a(A * a) { ... } 
主程序

Register r; 
A * a1 = new A(); 
customize_a(a1); 
r->register(a1); 

我一定要Ÿ我不喜欢那么多:/

2)过载主程序删除运营商B类的插件,

class B : public A { 
    ... 
    static void operator delete(void * ptr) { 
     ::operator delete(ptr); 
    } 
} 

Register r; 
A * a1 = get_customized_a(); 
r->register(a1); 

然而,我从来没有过载operator delete之前,所以我不知道这是否会甚至工作(如预期)。

3)是否有我错过任何方法?有更好的解决方案吗?

谢谢大家。

+2

'A'是否有虚拟dtor?另外,你在哪个平台上? – Deduplicator 2014-10-04 18:50:51

+0

不,如果我正确地阅读源代码,'A'没有虚拟驱动器。平台是Linux,AMD64 – Paladin 2014-10-04 18:56:39

+0

如果是Linux,你实际上不需要特别关心模块边界。尽管通过没有虚拟dtor的基本类型删除仍然保持UB。 – Deduplicator 2014-10-04 18:58:29

回答

0

如果通过使用一个指针到基类中的一个删除类然后析构函数必须是在基类虚拟无论对象的创建和销毁的位置。模块边界与此无关。

在如果一个对象的结构和销毁不同模块发生另一方面,它是只有当这些模块的新和删除操作者从不同池分配/解除分配存储器的问题。例如,如果这些模块与运行时库的不同版本链接,则可能是这种情况。类似的情况是,当您显式重写库中的新操作符和删除操作符时,您不需要在这些模块的分配器代码之间进行合作,而是从不同的池中进行分配。在这种情况下,虚拟析构函数并不能解决问题,因为对象的构造使用一个模块的分配器/池来分配内存,并且调用另一个模块的delete操作符来尝试从完全不同的对象中释放对象池。结果通常是错误的“堆损坏”运行时错误或类似的东西。

如果您遇到过上述问题,那么正确的解决方案是在基类中引入虚拟析构函数和虚拟Release()方法。 Release()方法应该删除丑陋的对象delete this;。将构造的对象传递给外部模块时,它必须通过调用Release()方法而不是直接删除对象来删除该对象,这样Release()方法将删除具有拥有该类和该实例的模块的delete操作符的对象。通常这种技术与智能指针相结合,以便外部模块通过调用Release()方法引用具有智能指针的这些对象,智能指针自动删除对象。

+0

这并不能真正帮助我。我不能改变对象的保存方式(==不能切换到智能指针)或删除(==不能通过'Release()'强制删除),我不能控制该代码。 – Paladin 2014-10-05 14:26:13

+0

@Paladin我写了这个答案只是为了澄清事情。你的问题有很多好的解决方案,我的文章没有把路径缩小到一个单一的黄金模式。如果你的基类包含一个虚拟的dtor,那么你不必改变它的任何东西,因为这两个模块都是从同一个堆中分配/释放的。这个解决方案不与我的文章中的陈述相冲突。 – pasztorpisti 2014-10-05 16:40:12