我有一个带有构造函数和析构函数的类。我们将该班级称为学生。
假设有一些代码
{
//some code
Student student;
//signal , may be ctrl+c or any other signal
}
我想要的是在引发信号时,我仍然希望调用学生对象的析构函数。
我对信号有一些基础知识,但是我不知道该怎么做。
我知道这可能是特定于o / s的;我正在尝试Linux。
答案 0 :(得分:1)
在C ++中很难做到这一点。
您需要handle the signal。处理程序需要将事件通知程序。程序需要通过干净地关闭来做出反应(通常是通过引发异常来完成;自然,您的程序必须行使异常安全性才能起作用)。
问题出在通知反应序列上。没有做到这一点的好方法。根据标准,允许信号处理程序执行的 only 唯一操作是设置sig_atomic_t
类型的全局变量并返回。因此,程序必须定期检查该变量的值,并在其值更改时做出反应。
某些操作系统可能允许在异常处理程序中执行更多安全的操作。但是,抛出C ++异常通常不是其中之一。信号本质上是异步的。如果在对象处于构造中间时交付该对象,则抛出异常可能会使它的状态不一致。
因此,唯一的方法是在程序的任何位置插入检查,或者如果它具有集中式IO或事件循环,则在此处插入检查。
答案 1 :(得分:0)
根据标准,您也可以呼叫std::quick_exit
。您需要向std::at_quick_exit
注册一些清除功能。例如:
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <set>
#include <signal.h>
#include <unistd.h>
using namespace std;
class K
{
public:
K(const char* _name)
: name(_name)
{ cout << "K() for " << name << endl; registered.emplace(this); }
~K()
{ cout << "~K() for " << name << endl; registered.erase(this); }
static void cleanup()
{
for (auto i: registered) {
i->~K();
}
}
private:
const char* name= nullptr;
static set<K*> registered;
};
set<K*> K::registered;
extern "C"
void signalHandler(int sig, siginfo_t* info, void* context)
{
if (sig == SIGKILL || sig == SIGTERM || sig == SIGINT) {
quick_exit(1);
}
return;
}
K k1("global k1");
int main(int argc, const char* argv[])
{
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_sigaction= signalHandler;
act.sa_flags= SA_SIGINFO;
sigemptyset(&act.sa_mask);
for (int i= 0; i < 64; i++) {
sigaction(i, &act, nullptr);
}
K k2("local k2");
at_quick_exit(K::cleanup);
for (int i= 0; i < 10; i++) {
cout << "." << flush;
sleep(1);
}
cout << "Normal exit" << endl;
return 0;
}