Управление деструктором-одиночкой - PullRequest
6 голосов
/ 09 августа 2011

Следующий небольшой пример реализует шаблон синглтона, который я видел много раз:

#include <iostream>

class SingletonTest {
private:
  SingletonTest() {}
  static SingletonTest *instance;
  ~SingletonTest() {
    std::cout << "Destructing!!" << std::endl;
  }

public:
  static SingletonTest *get_instance()  {
    if(!instance) instance = new SingletonTest;
    return instance;
  }
};

SingletonTest *SingletonTest::instance = 0;

int main(int argc, char *argv[]) {
  SingletonTest *s = SingletonTest::get_instance();

  return 0;
}

Основная проблема, с которой я столкнулся, заключается в том, что деструктор моего синглтона никогда не вызывается.

Вместо этого я могу сделать instance a (c ++ 0x?) shared_ptr, что хорошо работает - за исключением того, что это означает, что мой деструктор должен быть публичным.

Я мог бы добавить статическую очисткуметод, но это открывает возможность ошибки пользователя (т. е. забыть вызвать его).Это также не позволило бы выполнить надлежащую очистку перед лицом (необработанных) исключений.

Существует ли общая стратегия / шаблон, который позволит выполнять ленивую реализацию, «автоматически» вызывать мой деструктор и все же позволять мне сохранятьчастный деструктор?

Ответы [ 6 ]

20 голосов
/ 09 августа 2011

... не совсем прямой ответ, но слишком длинный для комментария - почему бы не сделать синглтон таким образом:

class SingletonTest {
private:
  SingletonTest() {}
  ~SingletonTest() {
    std::cout << "Destructing!!" << std::endl;
  }

public:
  static SingletonTest& get_instance()  {
    static SingletonTest instance;
    return instance;
  }
};

Теперь у вас есть ленивый синглтон, который будет разрушен при выходе ..Это не менее реентрантно, чем ваш код ...

2 голосов
/ 09 августа 2011

Вы можете написать функцию деинициализации и вызвать atexit() внутри конструктора объекта, чтобы зарегистрировать ее. Затем, когда C ++ во время выполнения деинициализирует модуль, он будет в некоторый момент после того, как main () вызовет вашу функцию деинициализации. Этот жирный курсив есть, потому что вы получаете довольно свободный контроль над тем, когда именно он вызывается, и это может привести к фиаско порядка деинициализации - будьте осторожны.

1 голос
/ 09 августа 2011

если вы объявите класс, который выполняет действительную операцию delete, как друга (пусть это будет shared_ptr<SingletonTest> или какой-либо тип удаления по умолчанию) как друга, ваш деструктор может быть закрытым.Хотя я не вижу необходимости делать это частным.

1 голос
/ 09 августа 2011

Вы можете использовать приватный деструктор с shared_ptr, передав ему средство удаления, имеющее доступ к деструктору (например, класс, определенный как член SingletonTest).

Однако вы должны быть очень осторожны при уничтожении синглетонов, чтобы гарантировать, что они не будут использованы после их уничтожения. Почему бы просто не использовать простую глобальную переменную?

1 голос
/ 09 августа 2011

Вы всегда можете подружиться с shared_ptr (или, скорее, scoped_ptr, который более подходит), чтобы предоставить ему доступ к вашему личному деструктору.

Обратите внимание, что есть также системная функция atexit(), которая может зарегистрировать функцию для вызова в конце приложения. Вы можете передать статическую функцию вашего синглтона, которая просто выполняет delete instanance;.

Обратите внимание, что обычно хорошей идеей является разделение класса, который должен быть одноэлементным, от его одноэлементности. Особенно для тестирования и / или когда вам нужен дублет. :)

Пока я в этом, стараюсь избегать ленивой инициализации. Инициализируйте / создайте свои синглтоны при запуске в четко определенном порядке. Это позволяет им корректно завершать работу и разрешать зависимости без сюрпризов. (У меня был циклический одиночный ад ... это проще, чем вы думаете ...)

0 голосов
/ 09 августа 2011

Первый вопрос: хотите ли вы, чтобы синглтон был разрушен.Разрушение синглтона может привести к проблемам разрушения;и так как вы закрываете, деструктор не может быть необходим для поддержки программных инвариантов.Единственный раз, когда вы хотите запустить деструктор синглтона, это если он управляет ресурсами, которые система не будет автоматически очищать, например, временными файлами.В противном случае лучше не вызывать деструктор.

Учитывая, что, если вы хотите, чтобы деструктор вызывался, есть две альтернативы: объявить отдельный объект как статическую локальную переменную в instanceфункции, или используйте std::auto_ptr или что-то подобное, вместо необработанного указателя, в качестве указателя на него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...