【发布时间】:2015-05-27 03:49:13
【问题描述】:
我正在阅读很久以前写的一些论坛帖子,并遇到了这样的问题:
如何创建一个对象,以便向它传递一个回调函数,并且当对象被销毁时,回调函数总是被执行?
我知道这个回调函数应该放在 RAII 之后的析构函数中。并且有人发布了这个问题的解决方案代码如下
class MyClass {
public:
MyClass(void (*cb)()) : done(cb) {}
~MyClass() {
if (done) {
try {
(*done)();
}
catch (...) {
// choice of exit, log, throw an alert to somewhere in the
//system, or ignore
}
}
}
private:
void (*done)();
};
但不知何故,我对这段代码感到不舒服。
- 因为通常建议不要在析构函数中使用
throw,但至少在这段代码中是否可以,因为整个try、catch块都在析构函数中? - 不知何故,我觉得在析构函数中取消引用指针是不安全的,因为当已经抛出另一个异常时,指针指向的对象可能在堆栈展开期间处于无效状态。但是在这段代码中,指向的函数是一个成员函数,并且在析构函数中检查了这个指针,那么在这种情况下是否完全可以?
- 还有比这段代码更好的解决方案吗?
【问题讨论】:
-
错了,析构函数是回调。
-
"由于指针指向的对象在堆栈展开期间可能处于无效状态,此时已经抛出了另一个异常" - 此示例中的回调仅接受独立函数,而不是对象,因此无需担心对象状态。另一方面,回调可能会在内部访问其他对象,但这是另一回事。
-
@RemyLebeau,你认为如果析构函数调用一个不是类成员的函数指针可以吗?
-
您还需要注意,当进入析构函数时,对象现在严格属于包含该析构函数的类。这意味着从析构函数调用的任何虚方法都将调用当前类中范围内的虚方法,而忽略派生类中的任何覆盖。
-
@EJP:这与使用回调函数有什么关系?除非调用对象将其
this指针传递给回调,但本讨论中的示例没有这样做。