Какая польза от деструктора как частного?

Ответы:

177

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

Например, если вы выполняете какие-то действия по подсчету ссылок, вы можете поручить объекту (или менеджеру, который был «другом») отвечать за подсчет количества ссылок на себя и удалять его, когда число достигает нуля. Частный dtor будет препятствовать тому, чтобы кто-либо еще удалил его, когда на него все еще были ссылки.

В другом случае, если у вас есть объект, у которого есть менеджер (или он сам), который может уничтожить его или может отказаться от его уничтожения в зависимости от других условий в программе, таких как открытое соединение с базой данных или запись файла. В классе или у менеджера может быть метод request_delete, который проверит это условие, удалит или отклонит его и вернет состояние, сообщающее, что он сделал. Это гораздо более гибко, чем просто вызывать «удалить».

Пол Томблин
источник
73

Такой объект никогда не может быть создан в стеке. Всегда в куче. И удаление должно быть сделано через друга или члена. Продукт может использовать единственную иерархию объектов и пользовательский менеджер памяти - в таких сценариях может использоваться частный dtor.

#include <iostream>
class a {
    ~a() {}
    friend void delete_a(a* p);
};


void delete_a(a* p)  {
    delete p;
}

int main()
{
    a *p = new a;
    delete_a(p);

    return 0;
}
dirkgently
источник
19
Исправление: такой объект может быть создан в стеке (но только в рамках друга или самого себя).
Томас Эдинг
Кроме того, он не может содержать статический или глобальный объект (т. Е. Иметь «статическую длительность хранения») в размещенной реализации (поскольку деструктор будет вызываться при выходе из программы).
Питер - Восстановить Монику
45

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

http://blogs.msdn.com/larryosterman/archive/2005/07/01/434684.aspx приводит пример, в котором объект имеет счетную ссылку и должен уничтожаться только самим объектом, когда счетчик становится равным нулю.

Майкл
источник
17

COM использует эту стратегию для удаления экземпляра. COM делает деструктор приватным и предоставляет интерфейс для удаления экземпляра.

Вот пример того, как будет выглядеть метод Release.

int MyRefCountedObject::Release() 
{
 _refCount--;
 if ( 0 == _refCount ) 
 {
    delete this;
    return 0;
 }
 return _refCount;
}

COM-объекты ATL являются ярким примером этого шаблона.

Виней
источник
8

Добавление к ответам, уже присутствующим здесь; частные конструкторы и деструкторы весьма полезны при реализации фабрики, где созданные объекты должны быть размещены в куче. Объекты, как правило, создаются / удаляются статическим членом или другом. Пример типичного использования:

class myclass
{
public:
    static myclass* create(/* args */)  // Factory
    {
        return new myclass(/* args */);
    }

    static void destroy(myclass* ptr)
    {
        delete ptr;
    }
private:
    myclass(/* args */) { ... }         // Private CTOR and DTOR
    ~myclass() { ... }                  // 
}

int main ()
{
    myclass m;                          // error: ctor and dtor are private
    myclass* mp = new myclass (..);     // error: private ctor
    myclass* mp = myclass::create(..);  // OK
    delete mp;                          // error: private dtor
    myclass::destroy(mp);               // OK
}
навигационный
источник
7

Класс может быть удален только сам по себе. Полезно, если вы создаете некоторый объект подсчета ссылок. Тогда только метод release может удалить объект, что поможет вам избежать ошибок.

Роланд Рабиен
источник
3

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

class Handler
{
public:
    virtual void onClose() = 0;
protected:
    virtual ~Handler();
};

class HandlerHolder
{
public:
    void setHandler( Handler* );
    Handler* getHandler() const;
protected:
    ~HandlerHolder(){}
private:
    Handler* handler_;
};

class GuiWindow : public HandlerHolder
{
public:
    void finish()
    {
        getHandler()->onClose();
    }

    virtual ~GuiWindow(){}
};
Николай Голубев
источник
3

Диркгентно это неправильно. Вот пример объекта с частным c-tor и d-tor, созданным в стеке (здесь я использую статическую функцию-член, но это также можно сделать с помощью функции Friend или класса Friend).

#include <iostream>

class PrivateCD
{
private:
    PrivateCD(int i) : _i(i) {};
    ~PrivateCD(){};
    int _i;
public:
    static void TryMe(int i)
    {
        PrivateCD p(i);
        cout << "inside PrivateCD::TryMe, p._i = " << p._i << endl;
    };
};

int main()
{
    PrivateCD::TryMe(8);
};

Этот код будет выводить: внутри PrivateCD :: TryMe, p._i = 8

misicd
источник
3
Я почти уверен, что это означает, что код, использующий ваш класс, не может создать экземпляр класса в стеке. Конечно, вы все еще можете создать экземпляр класса в стеке внутри методов класса, поскольку в этом контексте вы можете получить доступ к частным членам.
Эдвард Лопер
2

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

Джаред Оберхаус
источник