Деструктор для статических полей. Реализация Синглтона

Итак, классическая простая реализация Синглтона следующая:

class Singleton
{
private:
    static Singleton* singleton;
    Singleton() {}
public:
    static Singleton* getInstance();        
};

CPP файл

Singleton* Singleton::singleton = 0;

Singleton* Singleton::getInstance()
{
    if (!singleton)
    {
        singleton = new Singleton;
    }

    return singleton;
}

Я вижу утечку памяти здесь - "потому что нет нового для нового. Но в С++ нет статического деструктора, поэтому мы просто не заботимся об этой утечке памяти?

Ответ 1

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

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

Тем не менее, код, который вы указали выше, не соответствует тому, как большинство людей будут применять синглтон. Каноническая реализация С++ будет примерно такой:

class Singleton
{
private:
    /* No instantiation. */
    Singleton() {}

    /* Explicitly disallow copying. */ 
    Singleton(const Singleton&) = delete;
    Singleton& operator= (const Singleton&) = delete;

    /* In C++03, the above would be written as
     *
     *    Singleton(const Singleton&);
     *    Singleton& operator= (const Singleton&);
     * 
     * and you'd just leave the methods unimplemented.
     */
public:
    static Singleton& getInstance();        
};

.cpp файл:

Singleton& Singleton::getInstance() {
    /* Have a static local variable representing the unique instance.  Since
     * it static, there is only one instance of this variable.  It also only
     * initialized when getInstance is called.
     */
    static Singleton theInstance;
    return theInstance;
}

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

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

Надеюсь, это поможет!

Ответ 2

Почему вам следует избегать такого кода, если нет соответствия delete для new

Пока нет реальной утечки памяти (в большинстве современных операционных систем), хуже всего то, что ваш деструктор Singleton не вызван. И если вы приобретете некоторые ресурсы, они, вероятно, будут течь.

Что можно сделать здесь

Используйте интеллектуальный указатель для хранения экземпляра, рассмотрите std::unique_ptr (с С++ 11) или boost::auto_ptr

Ответ 3

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