Синглтон Деструкторы - PullRequest
27 голосов
/ 08 ноября 2008

Должны ли объекты Singleton, которые не используют счетчики экземпляров / ссылок, считаться утечками памяти в C ++?

Без счетчика, который вызывает явное удаление экземпляра синглтона, когда счетчик равен нулю, как объект удаляется? Очищается ли она ОС после завершения работы приложения? Что если этот синглтон выделил память в куче?

В двух словах, должен ли я вызывать деструктор Сингелтона или я могу рассчитывать на его очистку после завершения работы приложения?

Ответы [ 12 ]

20 голосов
/ 08 ноября 2008

Как часто, "это зависит". В любой операционной системе, достойной этого имени, при выходе из процесса вся память и другие ресурсы, используемые локально внутри процесса, будут освобождены. Вам просто не нужно беспокоиться об этом.

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

RAII поможет вам здесь. Если у вас есть такой сценарий:

class Tempfile
{
Tempfile() {}; // creates a temporary file 
virtual ~Tempfile(); // close AND DELETE the temporary file 
};

Tempfile &singleton()
{
  static Tempfile t;
  return t;
}

... тогда вы можете быть уверены, что ваш временный файл будет закрыт и удален при выходе из приложения. Однако это НЕ ориентировано на многопотоковое исполнение, и порядок удаления объектов может отличаться от ожидаемого или требуемого.

однако, если ваш синглтон реализован как ЭТОТ

Tempfile &singleton()
{
  static Tempfile *t = NULL;
  if (t == NULL)
    t = new Tempfile(); 
  return *t;
}

... тогда у вас другая ситуация. Память, используемая вашим временным файлом, будет восстановлена, но файл НЕ будет удален, потому что деструктор не будет вызван.

14 голосов
/ 08 ноября 2008

Вы можете рассчитывать на его очистку операционной системой.

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

Если вы используете синглтон, который лениво выделяется (то есть с идиомой блокировки тройной проверки) в языке, подобном с ++, с реальными деструкторами, а не с финализаторами, то вы не можете полагаться на его деструктор, вызываемый во время завершения работы программы. Если вы используете один статический экземпляр, то деструктор запустится после того, как main завершит работу в какой-то момент.

Независимо от того, когда процесс заканчивается, вся память возвращается в операционную систему.

11 голосов
/ 08 ноября 2008

Вы должны явно очистить все ваши объекты. Никогда не полагайтесь на ОС, чтобы очистить для вас.

Я обычно использую синглтон для инкапсуляции контроля над чем-то вроде файла, аппаратного ресурса и т. Д. Если я не очищаю это соединение должным образом, я могу легко утечь системные ресурсы. При следующем запуске приложения оно может завершиться ошибкой, если ресурс все еще заблокирован предыдущей операцией. Другая проблема может заключаться в том, что любая финализация, такая как запись буфера на диск, может не произойти, если она все еще существует в буфере, принадлежащем одноэлементному экземпляру.

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

9 голосов
/ 08 ноября 2008

Каждый язык и среда будут отличаться, хотя я согласен с @Aaron Fisher, что синглтон имеет тенденцию существовать на протяжении всего процесса.

В примере C ++, используя типичную одноэлементную идиому:

Singleton &get_singleton()
{
   static Singleton singleton;
   return singleton;
}

Экземпляр Singleton будет создан при первом вызове функции, и в том же экземпляре будет вызван деструктор во время фазы глобального статического деструктора при завершении работы программы.

3 голосов
/ 08 ноября 2008

Как вы создаете объект?

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

Например, программа

#include <iostream>

class Test
{
    const char *msg;

public:

    Test(const char *msg)
    : msg(msg)
    {}

    ~Test()
    {
        std::cout << "In destructor: " << msg << std::endl;
    }
};

Test globalTest("GlobalTest");

int main(int, char *argv[])
{
    static Test staticTest("StaticTest");

    return 0;
}

Распечатывает

In destructor: StaticTest 
In destructor: GlobalTest
3 голосов
/ 08 ноября 2008

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

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

В любом случае, если приложению отправляется сигнал Unix (т.е.: SIGTERM или SIGHUP ), поведение по умолчанию - завершить процесс без вызова деструкторов статических распределенных объектов (синглетонов) , Чтобы преодолеть эту проблему для этих сигналов, можно использовать обработчик, вызывающий выход, или избавиться от выхода таким обработчиком - signal(SIGTERM,exit);

2 голосов
/ 08 ноября 2008

Фольклор - явное освобождение глобальных выделений памяти перед завершением работы приложения. Я полагаю, что большинство из нас делают это по привычке, и потому, что мы чувствуем, что плохо «забыть» о конструкции. В мире Си есть закон симметрии, что любое распределение должно иметь где-то освобождение. Программисты C ++ думают иначе, если они знают и практикуют RAII.

В старые добрые времена, например, AmigaOS были РЕАЛЬНЫЕ утечки памяти. Если вы забыли освободить память, она НИКОГДА не станет доступной снова до перезагрузки системы.

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

1 голос
/ 21 декабря 2016

В таких языках, как C ++, в которых отсутствует сборка мусора, рекомендуется очистить перед завершением. Вы можете сделать это с классом друзей-деструкторов.

class Singleton{
...
   friend class Singleton_Cleanup;
};
class Singleton_Cleanup{
public:
    ~Singleton_Cleanup(){
         delete Singleton::ptr;
     }
};

Создание класса очистки при запуске программы, а затем при выходе из деструктора будет называться очисткой синглтона. Это может быть более многословно, чем позволить ему перейти к операционной системе, но оно следует принципам RAII и в зависимости от ресурсов, выделенных в вашем одноэлементном объекте, может оказаться необходимым.

1 голос
/ 08 ноября 2008

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

Деструктор вашей синглтон-оболочки должен удалить экземпляр, он не является автоматическим. Если он просто выделяет память и не использует ресурсы ОС, то нет никакого смысла.

1 голос
/ 08 ноября 2008

A singleton будет одним экземпляром вашего объекта. Вот почему он не требует счетчика. Если он будет существовать для длины вашего приложения, тогда деструктор по умолчанию будет в порядке. В любом случае память будет возвращена операционной системой после завершения процесса.

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