-
11. Data: 2009-08-02 12:09:39
Temat: Re: [c++]Prosta klasa, czemu nie wywołuje destruktorów?
Od: Bronek Kozicki <b...@s...net>
P.K.D wrote:
> Tutaj ta klasa: http://paste.dprogramming.com/dpibqklz
>
> Chodzi o to, że gdy wywołuję server::clearList() to obiekty nie są
> niszczone, chyba dlatego, że lista zawiera wskaźniki. Jak to zrobić i
> czy da się zrobić tak, że destruktory będą wywoływane automatycznie?
1. żeby wiedzieć kiedy będą wołane destruktory, najlepiej polegać na
jakimś sprytnym wskaźniku oraz wiedzieć kto kiedy jest właścicielem
obiektu. Poniżej stosuję boost::shared_ptr (bo bardziej popularny i
obsługuje klasy niekompletne), ale równie dobrze możesz stosować coś
innego. "dzielenie własności" wbrew nazwie "shared" jest tutaj potrzebne
tylko do zapewnienia semantyki płytkiego kopiowania i do niczego więcej
- boost::intrusive_ptr czy własne wynalazki w tej roli też się sprawdzą.
2. skoro już masz zagwarantowane wyłanie destruktora, to martw się o
notyfikację obiektu który trzyma listę obiektów żeby je z tej listy
skasował. Ja korzystam z funkcji UnregisterMyClass wołanej z
destruktora. Jedyny wyjątek kiedy tego nie trzeba robić - wtedy kiedy
lista jest i tak w całości kasowana. Oczywiście "lista" jest umowna -
rownie dobrze może być mapa albo jakaś własna, bardziej złożona
struktora. Ja w przykładzie niżej korzystam z std::list , dla zachowania
jasności.
3. do tego wszystkiego idealnie pasuje Pimpl, bo daje tanią samantykę
kopiowania i jednoznaczną własność wskaźnika. Jeżeli nie wiesz co to
jest : http://www.gotw.ca/publications/mill04.htm
Poniżej przykładowy kod.
// creator.hpp
class MyClass
{
class Impl;
boost::shared_ptr<Impl> pimpl_;
// zwiększamy ekapsulacje ograniczajac dostep do c-tora
friend class Creator;
explicit MyClass (boost::shared_ptr<Impl> p) : pimpl_(p) {}
public:
// opcjonalnie takie dwie funkcje, jeżeli potrzebne
void Unregister();
bool IsRegistered() const;
// dalej typowy Pimpl
// ...
};
class Creator
{
// zwiekszamy enkapsulacje ograniczajac dostep do UnregisterMyClass
friend class MyClass;
void UnregisterMyClass(const MyClass::Impl& my);
std::list<const MyClass::Impl*> myclassobjects_;
// ... może być więcej podobnych kolekcji ...
public:
~Creator();
MyClass MakeMyClass(parametry konstruktora);
// ...
};
// creator.cpp
#include "creator.hpp"
class MyClass::Impl : public IMogeSobieDziedziczycCokolwiek
// noncopyable - bo nie obiecujemy więcej niż musimy zaimplementować
, boost::noncopyable
{
Creator& creator_;
bool registered_;
// ...
public:
Impl(Creator& c, parametry konstruktora)
: creator_(c)
, registered_(true)
// ... inicjalizacja innych pól, definicja konstruktora
~Impl() {Release(true);}
void Release(bool unregister)
{
if (unregister && registered_)
{
creator_.UnregisterMyClass(*this);
registered_ = false;
}
}
// opcjonalnie takie dwie funkcje
void Unregister() {Release(true);}
bool IsRegistered() const {return registered_;}
// dalej typowy Pimpl
// ...
};
bool MyClass::IsRegistered() const {return pimpl_->IsRegistered();}
void MyClass::Unregister() {pimpl_->Unregister();}
// dalej typowy Pimpl, tzn podobnie jak IsRegistered i Unregister
// ...
// ...
// zarządzanie kolekcja obiektów MyClass przez klasę Creator :
void Creator::UnregisterMyClass(const MyClass::Impl& my)
{
myclassobjects_.remove(&my);
};
MyClass Creator::MakeMyClass(parametry konstruktora)
{
boost::shared_ptr<MyClass::Impl> p(new MyClass::Impl(*this,
parametry konstruktora));
// Nie trzymamy MyClass::Impl na własność - przekazujemy do MyClass !
myclassobjects_.push_back(p.get());
return MyClass(p);
}
Creator::~Creator()
{
// Kazemy obiektom MyClass::Impl sie "zwolnić", zeby oszczędzić
// wołanie UnregisterMyClass z destruktorów
std::for_each(myclassobjects_.begin(), myclassobjects_.end(),
boost::bind(&MyClass::Impl::Release, _1, false));
myclassobjects_.clear();
}
I jeszcze jedna uwaga - NIE NADUŻYWAĆ , bo nie jest wcale takie proste.
Ale jest za to dosyć niezawodne - jeżeli potrzebujemy mieć kolekcję
obiektów które skonstruowaliśmy.
B.
--
Remove -trap- when replying. Usun -trap- gdy odpisujesz.