我正在使用具有类Register的框架,我可以在其中注册A的实例。
Register r;
A * a1 = new A();
r->register(a1);
A * a2 = new A();
r->register(a2);
注册将获得所有权,并在超出范围时删除所有已注册的A
。
我想修改共享库中的A
行为(在我的情况下是.so),所以,我会做这样的事情(在库中):
class B : public A {
...
}
B * get_customized_a() {
return new B();
}
然后在主程序中
Register r;
A * a1 = get_customized_a();
r->register(a1);
但现在a1
将在主程序中删除,而不是在库中删除!我认为这是一个很大的禁忌。
那么如何解决这个问题?
我想出了两个解决方案:
1)使用A并通过独立功能
进行自定义 插件中的:
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)我错过了任何方法吗?有更好的解决方案吗?
谢谢大家。
答案 0 :(得分:0)
如果使用指向其中一个基类的指针删除类,则无论对象创建和销毁的位置如何,析构函数都必须在基类中是虚拟的。模块边界与此无关。
另一方面,如果对象的构造和销毁发生在不同的模块中,那么只有当这些模块的new和delete运算符从不同的池分配/释放内存时才会出现问题。例如,如果这些模块链接到运行时库的不同版本,则可能就是这种情况。类似的情况是当您显式覆盖库中的new和delete运算符并自己从不同的池执行分配时,这些模块的分配器代码之间没有合作。在这种情况下,虚拟析构函数不会使您免于出现问题,因为对象的构造使用一个模块的分配器/池分配内存,并且您调用另一个模块的delete运算符,该模块尝试从对象释放对象完全不同的游泳池结果通常是错误的"堆腐败"运行时错误或类似的东西。
如果您遇到上述问题,那么正确的解决方案是在基类中引入虚拟析构函数和虚拟Release()
方法。 Release()
方法应该删除带有丑陋delete this;
的对象。当您将构造的对象传递给外部模块时,它必须通过调用Release()
方法而不是直接删除它来删除该对象,这样Release()
方法使用删除操作符删除该对象。拥有类和实例的模块。通常这种技术与智能指针相结合,以便外部模块使用智能指针引用这些对象,通过调用Release()
方法自动删除对象。