Оказывается, это было очень плохой идеей. Количество странных вещей огромно.
Что происходило
Для shared_ptr в обработчике использовалось значение use_count, равное двум. Одна ссылка была в самом PidManager, другая была в клиенте PidManager. Вызов деструктора shared_ptr (~ PidManager ()) уменьшил значение use_count на единицу. Затем, как намекнул GMan, при вызове метода exit () вызывался деструктор для статически инициализированного экземпляра PidManagerPtr, уменьшая значение use_count до 0 и вызывая деструктор PidManager. Очевидно, что если бы PidManager имел более одного клиента, use_count не упал бы до 0, и это не сработало бы вообще.
Это также дает некоторые подсказки относительно того, почему вызов instance_.reset () не работал. Вызов действительно уменьшает количество ссылок на 1. Но оставшаяся ссылка - это shared_ptr в клиенте PidManager. Этот shared_ptr является автоматической переменной, поэтому его деструктор не вызывается при выходе (). Деструктор instance_ вызывается, но, поскольку он был сброшен (), он больше не указывает на экземпляр PidManager.
Решение
Я полностью отказался от использования shared_ptrs и решил вместо этого пойти с Майерсом Синглтоном. Теперь мой код выглядит так:
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_, поэтому никакой другой код очистки не требуется помещать в обработчик. Это позволяет аккуратно избежать проблем с вызываемым обработчиком, когда PidManager находится в несовместимом состоянии.