我有一个析构函数执行一些必要的清理(它会杀死进程)。即使将SIGINT发送到程序,它也需要运行。我的代码目前看起来像:
typedef boost::shared_ptr<PidManager> PidManagerPtr
void PidManager::handler(int sig)
{
std::cout << "Caught SIGINT\n";
instance_.~PidManagerPtr(); //PidManager is a singleton
exit(1);
}
//handler registered in the PidManager constructor
这样可行,但似乎有很多警告禁止显式调用析构函数。在这种情况下,这是正确的做法,还是有“更正确”的方法呢?
答案 0 :(得分:5)
如果该对象是单例,则不需要使用共享指针。 (只有一个!)
如果您将其切换为auto_ptr
,则可以在其上调用release()
。或者也许scoped_ptr
,呼叫reset()
。
这一切都说,我99%肯定exit()
将破坏静态构造的对象。 (我看哪些单身人士。)我所知道的是exit()
调用已注册的atexit()
函数。
如果您的单身人士没有通过退出自动销毁,那么在您的情况下要做的就是制作atexit
钩子:
void release_singleton(void)
{
//instance_.release();
instance_.reset();
}
// in main, probably
atexit(release_singleton);
答案 1 :(得分:2)
除非使用placement new构造对象,否则永远不要显式调用析构函数。 将清理代码移动到单独的函数中并调用它。从析构函数中调用相同的函数。
答案 2 :(得分:2)
事实证明,这样做是一个非常糟糕的主意。奇怪的事情发生了很多。
shared_ptr的use_count为2,进入处理程序。一个参考是在PidManager本身,另一个是在PidManager的客户端。调用shared_ptr(~PidManager())的析构函数会将use_count减少一个。然后,正如GMan所暗示的,当调用exit()时,调用静态初始化的PidManagerPtr instance_的析构函数,将use_count减少为0并导致调用PidManager析构函数。显然,如果PidManager有多个客户端,use_count就不会降到0,而这根本就不起作用。
这也提供了一些关于为什么调用instance_.reset()不起作用的提示。该调用确实将引用计数减少了1.但剩下的引用是PidManager客户端中的shared_ptr。 shared_ptr是一个自动变量,因此它的析构函数不会在exit()中调用。调用instance_析构函数,但由于它是reset(),因此它不再指向PidManager实例。
我完全放弃了使用shared_ptrs并决定改用Meyers Singleton。现在我的代码看起来像这样:
void handler(int sig)
{
exit(1);
}
typedef PidManager * PidManagerPtr
PidManagerPtr PidManager::instance()
{
static PidManager instance_;
static bool handler_registered = false;
if(!handler_registered)
{
signal(SIGINT,handler);
handler_registered = true;
}
return &instance_;
}
显式调用exit允许静态初始化的PidManager instance_的析构函数运行,因此不需要在处理程序中放置其他清理代码。这可以很好地避免在PidManager处于不一致状态时调用处理程序的任何问题。
答案 3 :(得分:1)
你真的不想在信号处理程序中做很多事情。最安全的事情就是设置一个标志(例如全局volatile bool),然后让程序的常规事件循环检查每隔一段时间标记一次,如果它已经变为true,则从那里调用清理/关闭例程。
因为信号处理程序与应用程序的其余部分异步运行,所以在信号处理程序内部执行的操作不是很安全 - 您可能想要与之交互的任何数据可能处于不一致状态。 (并且不允许你使用信号处理程序中的互斥锁或其他同步 - 信号非常邪恶)
但是,如果您不喜欢必须一直轮询布尔值,那么您可以在信号处理程序(至少在大多数操作系统中)中执行另一项操作是在套接字上发送一个字节。所以你可以提前设置一个socketpair(),并在套接字对的另一端有正常的事件循环select()(或其他);当它在该套接字上接收到一个字节时,它知道你的信号处理程序必须发送该字节,因此是时候清理了。
答案 4 :(得分:0)
另一种方法可能是动态分配单例(首次使用或主要分配),delete
进行清理。
的Eh。我猜你的PidManagerPtr实际上指的是一个动态分配的对象...但是不是boost :: shared_ptr实际上是在清理重新分配?所以它应该足够了:
instance_ = 0;
答案 5 :(得分:0)
只需在shared_ptr上调用reset(),它就会为你删除你的实例。