Najprostszy możliwy “sprytny” wskaźnik… ale czemu sprytny? Ano temu, że sam zwalnia pamięć, gdy “nadejdzie jego czas”…

Do czego mi się to przyda

Główne zastosowanie std::auto_ptr to zabezpieczenie się przed wyciekami pamięci. Już nie musisz usuwać każdego obiektu operatorem delete – obiekty usuną się same!

Jak to działa

Bardzo prosto. std::auto_ptr bierze pod opiekę nasz obiekt i sam usuwa go z pamięci w odpowiednim czasie. A kiedy jest ten “odpowiedni czas”? W momencie, kiedy sterowanie wychodzi poza zakres, w którym wskaźnik został zdefiniowany.

Przykład

Kod z wykorzystaniem zwykłego wskaźnika:

void example1()
{
	// 1. tworzymy obiekt
	Bubble* bubble = new Bubble();

	// 2. pracujemy z obiektem
	bubble->blowUp();

	// 3. usuwamy obiekt
	delete bubble;
}

Kod z wykorzystaniem sprytnego wskaźnika

void example2()
{
	// 1. tworzymy obiekt
	auto_ptr bubble(new Bubble());

	// 2. pracujemy z obiektem
	bubble->blowUp();

} // tutaj auto_ptr sam usunie nasz obiekt "bubble"

Jak widzisz, nie usuwamy już ręcznie obiektu. Spryny wskaźnik robi to za nas! Jednocześnie, korzystamy ze wskaźnika w dawny sposób. Fajne!

Co jeszcze mogę zrobić ze sprytnym wskaźnikiem

std::auto_ptr ma jeszcze kilka funkcji. Możesz:

  1. Pobrać od niego obiekt, którym się opiekuje:

    	// 1. tworzymy obiekt pod opieką sprytnego wskaźnika
    	auto_ptr<Bubble> auto_bubble(new Bubble());
    
    	// 2. pobieramy obiekt
    	Bubble *bubble = auto_bubble.get();

    Teraz masz dostęp do obietku poprzez sprytny wskaźnik auto_bubble, oraz poprzez goły wskaźnik bubble.

  2. Odebrać mu opiekę nad obiektem:

    	// 1. tworzymy obiekt pod opieką sprytnego wskaźnika
    	auto_ptr<Bubble> auto_bubble(new Bubble());
    
    	// 2. odbieramy sprytnemu opiekę nad obiektem
    	Bubble *bubble = auto_bubble.release();

    Teraz masz dostęp do obietku jedynie poprzez goły wskaźnik bubble. Uwaga – sam musisz zadbać o usunięcie obiektu, bo sprytny wskaźnik już się nim nie opiekuje!

  3. Oddać pod jego opiekę inny obiekt:

    	// 1. tworzymy obiekt pod opieką sprytnego wskaźnika
    	auto_ptr<Bubble> auto_bubble(new Bubble());
    
    	// 2. oddajemy pod opiekę sprytnego nowy obiekt
    	auto_bubble.reset(new Bubble());

    Uwaga – Poprzedni obiekt zostaje automatycznie zniszczony!

  4. Przekazać opiekę innemu sprytnemu wskaźnikowi:

    // 1. tworzymy obiekt pod opieką sprytnego wskaźnika
    auto_ptr<Bubble> auto_bubble1(new Bubble());
    
    // 2. przekazujemy opiekę nad obiektem innemu sprytnemu
    auto_ptr<Bubble> auto_bubble2 = auto_bubble1;

    Teraz auto_bubble2 posiada nasz obiekt, a auto_bubble1 ma NULL. Dlaczego? Bo gdyby oba opiekowały się naszym obiektem, to w pewnym momencie obiekt ten byłby usuwany dwa razy!

Do czego nie stosować std::auto_ptr

  • do kontenerów STL. Przykładowo, vector<auto_ptr<int> > to niedobry pomysł. Porządna implementacja STL sprawi, że taka konstrukcja nawet nie przejdzie kompilacji! Gdyby jednak przeszła – wystrzegaj się  jej. STL wymaga, aby obiekty mogły być kopiowane, a auto_ptr tylko przekazuje opiekę nad obiektem. Przez to, kiedy np. vector zrobi sobie kopię tymczasową jakiegoś swojego elementu, a potem ją usunie – usunie nasz jedyny obiekt!
  • do przekazywania parametrów do funkcji. Przykładowo, void funkcja(auto_ptr<string>) to niedobry pomysł. Podobnie jak w poprzednim przypadku – zamiast skopiowania obiektu na stos, nastąpi przekazanie opieki nad nim do tymczasowego obiektu (parametru funkcji). Wraz z zakończeniem funkcji parametr zostanie usunięty i tym samym przepadnie nasz obiekt. Zobacz artykuł Niebezpieczny auto_ptr

Podsumowanie

I  to tyle. Możesz już zacząć używać sprytnych wskaźników. Od dziś Twoje programy staną się lepsze, a Twoje życie – łatwiejsze ;)

PS. Klasa std::auto_ptr dostępna jest w nagłówku <memory>

Related Posts: