2009-03-10 6 views

ответ

139

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

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

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

+3

см СНИМИТЕ # 4 http://www.gotw.ca/publications/mill18.htm – Eric

+2

Этот aswer отсутствует некоторый пример кода. – mrgloom

43

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

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

5

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

55

Такой объект никогда не может быть создан в стеке. Всегда в куче. И удаление должно быть сделано через друга или участника. Продукт может использовать единую иерархию объектов и пользовательский менеджер памяти - в таких сценариях может использоваться частный 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; 
} 
+1

Удаление должно быть выполнено через друга/или участника/ – MSalters

+0

Это уже было сказано - поэтому я пропустил его. Будет ли updata (во втором чтении мое утверждение выглядит слишком сильным). – dirkgently

+12

Исправление: такой объект * может * быть создан в стеке (но только в области действия друга или самого себя). –

2

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

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(){} 
}; 
15

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

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

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

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

+0

Инструктивный. Благодаря! –

3

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

#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

+2

Я уверен, что dirkgently означает, что код, который * использует * ваш класс, не может создать экземпляр класса в стеке. Конечно, вы все равно можете создать класс класса * в методах * класса, поскольку в этом контексте вы можете получить доступ к закрытым членам. –

5

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

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 
}