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


Ответы:


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;
}

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


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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.