Intelignetne wskaźniki w C++11 – część 2

W poprzedniej części zajmowalismy się inteligentnym wskaźnikiem unique_ptr. Teraz przyszła kolej na dwa pozostałe wskażniki, zdefiniowane przez klasy shared_ptr oraz weak_ptr.

Klasa shared_ptr

Część obiektów używanych w programie posiada jednego właściela i jeden wskaźnik. Są jednak takie, które tych wskaźników mają wiele, aby były widoczne z różnych miejsc. Te odnośniki do obiektu z różnych miejsc w programie zapewnia klasa shared_ptr. Są oczywiście także zwykle wskaźniki i referencje, lecz one nie dają gwarancji usunięcia obiektu wraz z usunięciem ostatniego odwołania. Dlatego do standardu C++11 włączono także inteligentny wskaźnik implementujący pojęcie współdzielonej własności obiektu.

Obiekt, na który wskazuje shared_ptr jest zwalniany wraz z zasobami przez usunięcie ostatniego wskaźnika współdzielonego. Takie domyślne zwolnienie obiektu polega oczywiście na wywołaniu operatora delete na jego rzecz. Można ponadto zdefiniować własne operacje porządkowania stanu obiektu. Jeżeli obiekt został na przykład utworzony za pomocą new [], można go zwolnić za pomocą delete []. Ta automatyzacja usuwania następuje wtedy, gdy obiekt nie jest już używany.

Podobnie jak unique_ptr klasa ta udostęnia interfejs z operatorem wyłuskania * (zwraca obiekt wskazywany) a także operator dostępu do składowej obiektu wskazywanego -> (w przypadku, gdy ów obiekt jest strukturą lub klasą). Wskaźnik shared_ptr nie implementuje arytmetyki wskaźnikowej, która jest przez często uznawana za źródło problemów programistycznych.

Oto prosty przykład ilustrujący działanie wskaźnika:

#include <iostream>
#include <string> 
#include <memory>
#include <vector>

using namespace std; 

class Person{
   int i = 30;
   string name = Adam;
public:
   Person(const string s, const int i) : name(s), age(i){
   cout << "C++ message: obiekt Person utworzony." << endl;
   }

   ~Person(){
   cout << "C++ message: obiekt Person zniszczony." << endl;
   }

   void addition() const{
   cout << name << ", " << age << endl;
   }
}

int main(void){
   shared_ptr<Person> p (new Report("Wskaznik shared_ptr dziala."));
   p->addition();
}

Klasa weak_ptr

Jak można był zobaczy powyżej klasa shared_ptr eliminuje uciążliwe kontrolowanie czasu życia zasobów, w przypadku współdzielenia własności obiektu. Jednak zdarzają się takie sytuacje, kiedy to nie jest korzystne ani oczekiwane.

Pierwszą z takich sytuacji jest współdzielenie obiektu ale bez jego współwłasności, czyli sytuacja w której czas życia wskaźnika jest dłuższy, aniżeli czas życia obiektu wskazywanego. Wtedy użycie wskaźnika shared_ptr uniemożliwia zwolnienie zasobów, a użycie zwykłych wskaźników nie byłoby poprawne z powodu możliwych prób odwołań do obiektu już nieistniejącego.

Druga sytuacja, to odwołania cykliczne, w których dwa różne obiekty odwołują się do siebie za pomocą wskaźników shared_ptr i nigdy nie dojdzie do zwolnienia zasobów powiązanych z tymi obiektami, ponieważ nie dojdzie do wyzerowania liczby odwołań.

W takich sytuacjach można użyć klasy inteligentnego wskaźnika słabego weak_ptr, która pozwala na współdzielenie dostępu do obiektu wskazywanego bez przejmowania własności tego obiektu, a co za tym idzie bez odpowiedzialności za jego zwolnienie.

Ten typ wskaźnika nie jest dostępny poprzez operatory * oraz -> stąd korzystanie z tych operatorów wymaga konstrukcji wskaźnika współdzielonego shared_ptr.

Poniższy przykład pokazuje działanie wskaźnika:

#include <iostream>
#include <string>
#include <vector>
#include <memory>

using namespace std;

class Person {
  public:
    string name;
    shared_ptr<Person> mother;
    shared_ptr<Person> father;
    vector<shared_ptr<Person>> kids;

    Person (const string& n,shared_ptr<Person> m = nullptr,
            shared_ptr<Person> f = nullptr) : name(n), mother(m), father(f) {
    }

    ~Person() {
      cout << "Obiekt usuniety. " << name << endl;
    }
};

shared_ptr<Person> initFamily (const string& name)
{
    shared_ptr<Person> mom(new Person("mama "+name)); 
    shared_ptr<Person> dad(new Person("tata "+name)); 
    shared_ptr<Person> kid(new Person(name,mom,dad)); 
    mom->kids.push_back(kid);
    dad->kids.push_back(kid);
    return kid;
}

int main()
{
    shared_ptr<Person> p = initFamily("nico");

    cout << "rodzina Jasia istnieje" << endl;
    cout << "- Jasio jest współdzielony " << p.use_count() << "-krotnie" << endl;
    cout << "- imię pierwszego dziecka mamy Jasia: " 
         << p->mother->kids[0]->name << endl;

    p = initFamily("Adam");
    cout << "rodzina Adama istnieje" << endl;
}

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *