Warsztat » Forum

[C++] Samo-NULLujące smart pointery

Nov 18, 2009 | Liosan |
45 wypowiedzi na 3 stronach:
1 2 3
Liosan
Nov 18, 2009

Samo-NULLujące smart pointery

Zdravstvujte!

Na ostatnim warszawskim warsztatowym spotkaniu usłyszałem wzmiankę o wariancie smart pointerów, które po wykonaniu destruktora obiektu na który wskazują wszystkie przyjmują wartość NULL. Kluczowe tutaj jest słówko wszystkie - czyli każdy taki CVerySmartPtr<CWhatevah> foo; w każdym obiekcie czy funkcji nagle stanie się NULLem, jeśli wartość na którą pokazuje zostanie zdealokowana.

Po dłuższym przemyśleniu stwierdziłem, że to jest właśnie to co chciałbym dodać do mojego kodu C++ :) Podstawowy nurt implementacyjny jest prosty - trzymamy gdzieś adresy wszystkich obiektów CVerySmartPtr<>, wiemy kto na kogo pokazuje, i w momencie destrukcji przebiegamy się po tej liście i NULLujemy co trzeba. No dobra, tylko gdzie trzymamy te listy?

Jedna opcja to wewnątrz obiektu, w jakimś wektorze czy czymś; mało mi się to podoba, bo w większości przypadków tych wskaźników będzie mało, więc będę miał dużo śmieci w obiekcikach. Inna opcja to jedna statyczna lista per klasa (jedna dla CWhatevah, druga dla CBar itp); to wymaga trochę kombinowania (użycie nowej parametryzacji CVerySmartPtr<> to deklaracja nowej zmiennej statycznej w .cpp; to jest do przeżycia. Jeszcze inna opcja to wspólna, globalna lista - trochę kombinowania i można wspólnie trzymać adresy wszystkich rodzajów wskaźników. Thread-safety mnie nie interesuje (te obiekty nigdy nie bedą obsługiwane przez kilka wątków).

Inne pytanie, to jaką strukturę danych wybrać? Jeśli jest mało danych (jeden worek per obiekt) to wektor jest git, ale jeśli jest więcej to już można pomyśleć. Też wektor, i przebiegać wszystkie wskaźniki z każdym destruktorem? A może zestaw list na bloku pamięci? Plus jakiś indeks void* -> numer/adres listy? To i tak jest zamulające rozwiązanie, ale nie chcę całkiem dobić tym gry :)

Co sądzicie? :) Robiliście kiedyś coś takiego?

Liosan
Krzysiek K.
Nov 15, 2009

Odp: Samo-NULLujące smart pointery

Cytat:
Inne pytanie, to jaką strukturę danych wybrać?

Dwukierunkową listę cykliczną. Przynajmniej ja tak zrobiłem u siebie.
Kod: template<class _T>
struct volatile_ptr {
    _T   *ptr;
    volatile_ptr<_T>    *prev, *next;
    ...
};


Dodatkowo każdy obiekt powinien dziedziczyć po jakimś wspólnym potomku (CSmartPointerable?), który by miał taki wskaźnik na siebie i robił co trzeba w destruktorze. W momencie przypisania obiektu do volatile_ptr inteligentny wskaźnik by dopinał się do tej listy (w destruktorze wskaźnik oczywiście się odpina).

Dwukierunkowe listy cykliczne to bardzo fajne strukturki, bo wpinanie i wypinanie się obiektu z listy to operacje wręcz banalne, no i nie ma żadnych przypadków brzegowych (bo nie ma brzegów). :)
novo
Nov 15, 2009

Odp: Samo-NULLujące smart pointery

Ja robilem tak(pseudokod):

Kod: cpp]
template<typename T>
class Ptr{
  struct Desc{
    T     *ptr;
    uint  refCount;
  };
  public:
    // metody smart pointera
    bool isValid(){ return d && d->ptr; }
  private:
    Desc *d;
};


Kazdy smartpointer nie przechowuje wskaznika do obiektu na ktory wskazuje, tylko wskaznik na Desc ktory jest wspolny dla wszystkich pointerow wskazujacych dany obiekt. Wiec ustawiajac d->ptr=NULL efektywnie dla wszystkich pointerow jest NULL.

//EDIT: Minus tego rozwiazania jest taki, ze to zeruje tylko smart pointery stworzone przez kopiowanie pierwszego smartpointera, ale to najczesciej nie stanowi problemu bo ten "pierwszy" smart pointer siedzi w jakims wektorze i kolejne sa tworzone na jego podstawie(praktycznie wszystkie obiekty siedza u mnie w jakims kontenerze).
Krzysiek K.
Nov 16, 2009

Odp: Samo-NULLujące smart pointery

Cytat:
Kazdy smartpointer nie przechowuje wskaznika do obiektu na ktory wskazuje, tylko wskaznik na Desc ktory jest wspolny dla wszystkich pointerow wskazujacych dany obiekt. Wiec ustawiajac d->ptr=NULL efektywnie dla wszystkich pointerow jest NULL.

Trochę nie przypada mi do gustu dodatkowy dostęp pośredni przy każdym odwołaniu, ale koncepcja sama w sobie ciekawa. Problem w tym, że rozwiązując problem stworzyliśmy inny, bo kto i kiedy będzie usuwał obiekt Desc?
novo
Nov 16, 2009

Odp: Samo-NULLujące smart pointery

Obiekt desc jest usuwany kiedy refCount==0. Rozumiem, ze chodzi o sytuacje kiedy na obiekt wskazuje kilka smart pointerow i recznie kazemy mu sie zdeletowac, a nie czekamy az refCount spadnie do zera(bo wtedy nie powinno juz byc zadnych pointerow wskazujacych na obiekt, wiec problem nie istnieje). Desc jest wiec usuwany w momencie gdy ginie ostatni pointer uzywajacy tego Desc-a.
Liosan
Nov 16, 2009

Odp: Samo-NULLujące smart pointery

Cytat:
Dodatkowo każdy obiekt powinien dziedziczyć po jakimś wspólnym potomku (CSmartPointerable?), który by miał taki wskaźnik na siebie i robił co trzeba w destruktorze. W momencie przypisania obiektu do volatile_ptr inteligentny wskaźnik by dopinał się do tej listy (w destruktorze wskaźnik oczywiście się odpina).

Mhm, sorry, zapomniałem o tym wspomnieć - to jest ta prosta część :)

Cytat:
Dwukierunkowe listy cykliczne to bardzo fajne strukturki, bo wpinanie i wypinanie się obiektu z listy to operacje wręcz banalne, no i nie ma żadnych przypadków brzegowych (bo nie ma brzegów). :)

Ok, to sensowne; alokować węzły z puli, czy się nie opłaca? :)

Cytat:
kto i kiedy będzie usuwał obiekt Desc?

Właśnie miałem o to samo spytać...
Cytat:
Obiekt desc jest usuwany kiedy refCount==0.

... i to zasugerować :) Za szybcy jesteście :D

Liosan
Krzysiek K.
Nov 16, 2009

Odp: Samo-NULLujące smart pointery

Cytat:
Ok, to sensowne; alokować węzły z puli, czy się nie opłaca? :)

A czytałeś przykładowy fragment kodu powyżej? ;) Jak dla mnie nie opłaca się rozdzielać wskaźnika od węzła. Wskaźnik sam w sobie jest węzłem.

Cytat:
Desc jest wiec usuwany w momencie gdy ginie ostatni pointer uzywajacy tego Desc-a.

OK. Problem tylko pojawi się wtedy, gdy zaczniemy się bawić w DLLki, bo tracimy kontrolę nad tym, kto Desc usuwa.
novo
Nov 16, 2009

Odp: Samo-NULLujące smart pointery

Nie uzywalem dll'ek z tymi pointerami, wiec w ogole tego nie bralem pod uwage :)
Liosan
Nov 16, 2009

Odp: Samo-NULLujące smart pointery

Cytat:
A czytałeś przykładowy fragment kodu powyżej? ;) Jak dla mnie nie opłaca się rozdzielać wskaźnika od węzła. Wskaźnik sam w sobie jest węzłem.

Yyyy mea culpa. Za mało kawy :) To faktycznie robi sensa.

Liosan
Esidar
Nov 17, 2009

Odp: Samo-NULLujące smart pointery

Cały pomysł z tymi 'NULL'ami' brzmi bardzo ciężko... ale skoro na wydajności ci nie zależy:

Kod: 


T* _object_ptr = new object();
nullable_ptr<T> nptr( &_object_ptr );

....

smart_ptr<T> sptr( nullable_ptr<T>* nptr );

sptr->nullable_ptr->object_ptr->DoSomething();

sptr->nullable_ptr->object_ptr = NULL;


Dab
Nov 17, 2009

Odp: Samo-NULLujące smart pointery

Można to trochę uprościć.

Potrzebna są nam w zasadzie trzy funkcje. Jedna to [tt]RegisterPointer(void**)[/tt]. Dodatkowo możemy klasom dać statyczne konstruktory. Wtedy wyglądać to może tak:

Kod: class Foo
{
Bar *bar;
Biz *biz;
public:
Foo(Biz *biz_)
{
Bar::create(&bar);
biz = biz_;
RegisterPointer(&biz);
}
}


i tyle. Potem tylko w destruktorze Bar i Biz musimy odpalić drugą funkcję: [tt]DetachPointers(this);[/tt]
Ten sposób nie rozwiązuje problemu kasowania Bar na w odpowiednim momencie (bo to nie jest w ogóle problem), tylko wskaźników pokazujących na niego nawet po skasowaniu. W pisaniu UI gdzie jest masa delegatów jest to nieoceniona pomoc.

No i oczywiście te adresy wskaźników przy usuwaniu obiektu trzeba wyrejestrować (UnregisterPointers)

Oczywiście OOP-owi [s]terro[/s]puryści dorzucą tu szablony i trzy poziomy dziedziczenia smart pointerów. Wcale nie jest to potrzebne. To ma rozwiązywać jedynie konkretny problem, nie leczyć raka i AIDS. :)
Krzysiek K.
Nov 17, 2009

Odp: Samo-NULLujące smart pointery

Cytat:
Cały pomysł z tymi 'NULL'ami' brzmi bardzo ciężko... ale skoro na wydajności ci nie zależy

Ciężko? Moja propozycja nie będzie za bardzo ustępowała wydajnością zwykłym wskaźnikom (tylko w przypadku częstego kopiowania wskaźników jest mały dodatkowy narzut).
novo
Nov 18, 2009

Odp: Samo-NULLujące smart pointery

Co do DLL to w tym rozwiazaniu ktore podalem wystarczy zamiast usuwac obiekt desc, rejestrowac go gdzies do usuniecia:
Kod: cpp]
~Ptr(){
  if( !(--d->refCount) ){
    registerForDelete(d);
    d=0;
  }
}

Wtedy ma sie kontrole nad zwalnianiem tych Desc-ow. Generalnie jestem za tym, zeby obiekty Desc byly przydzielane z jakiejs puli, bo inaczej ma sie dodatkowa allokacje na kazdy tworzony smart pointer.
agent_J
Nov 19, 2009

Odp: Samo-NULLujące smart pointery

Zobaczcie sobie jak działa QPointer w Qt : :-X
msieradzki
Nov 17, 2009

Odp: Samo-NULLujące smart pointery

A jak się mają te wasze genialne pomysły do wielu wątków korzystających z tego naraz? :>
Krzysiek K.
Nov 17, 2009

Odp: Samo-NULLujące smart pointery

Cytat:
A jak się mają te wasze genialne pomysły do wielu wątków korzystających z tego naraz? :>

Nijak. Z założenia z czegoś takiego nie powinno korzystać wiele wątków, chociaż na dobrą sprawę nie ma przeszkód, żeby zrobić potrzebne operacje przez funkcje Interlocked*. Tak, czy inaczej, jeżeli kilka wątków ma się grzebać w tych samych danych, to najczęściej tylko świadczy o kiepskim podziale zadań między wątki.
Strony:
1 2 3