Blog programisty o C/C++ i PHP

Programowanie w C/C++ i PHP. Blog pełen wskazówek, porad, analiz i opisów.

Niebezpieczny std::auto_ptr

październik 11th, 2006 in Programowanie w C++, Programowanie w C/C++

W bibliotece standardowej umieszczona została bardzo przydatna klasa (oczywiście jak przystało na bibliotekę STL jest klasą wzorcową). Mówię tu o klasie inteligentnego wskaźnika std::auto_ptr. Jest to klasa o niezwykle wysokim stopniu przydatności, gdyż pozwala na automatyczne zwalnianie pamięci przydzielonej dynamicznie za pomocą operatora new.

Przypomnijmy sobie jak się to używa i jak to działa:
tworzymy następującą klasę:

class MyClass
{
	int m_id;

public:

	MyClass(int id) : m_id(id)
	{
		std::cout << "Utworzono obiekt numer " << m_id << std::endl;
	}

	~MyClass()
	{
		std::cout << "Obiekt numer " << m_id << " został usunięty" << std::endl;
	}
};

w kodzie programu umieszczamy taki oto prosty test:

{ // zakres 1 (ang. scope 1)

	std::auto_ptr pMyObject(new MyClass(1));
	{ // zakres 2 (ang. scope 2)
		std::auto_ptr pMyOtherObject(new MyClass(2));
	}
}

Jak widzisz mimo, że utworzyliśmy zmienną dynamiczną za pomocą new, nie korzystamy w ogóle z delete.

Dlaczego ? Ponieważ robi to za nas inteligentny wskaźnik. Odciąża nas od konieczności pamiętania o czynności usuwania zmiennych dynamicznych i jednocześnie upraszcza kod. Ma to szczególnie istotne znaczenie gdy mamy bardzo złożone struktury danych. Przecież i Ty i Ja nie jesteśmy maszynami i mamy prawo się pomylić czy o czymś zapomnieć.

Ile za to płacimy ? Otóż to udogodnienie kosztuje nas niewiele, gdyż klasa auto_ptr zajmuje mało miejsca i opłaca się jej używać nawet dla małych struktur (w takim przypadku zyskujemy komfort pomimo dodatkowej pamięci).

Istnieje jednak pewien kruczek, o którym powinieneś wiedzieć zanim zaczniesz używać tej klasy, a szczególnie wtedy, gdy obiekty tej klasy będą używane jako parametry funkcji/metod czy będą wartością zwracaną. Zwykłe przypisanie jednego wskaźnika automatycznego do drugiego to, jak pamiętasz uruchomi mechanizm klonowania obiektu poprzez operator przypisania. Klasa auto_ptr podczas kopiowania wydziedzicza wskazywany obiekt (czyli obiekt który utworzyliśmy za pomocą new).

Spójrz na przykład:

{ // zakres 1 (ang. scope 1)

	std::auto_ptr pMyObject(new MyClass(1));
	{ // zakres 2 (ang. scope 2)
		std::auto_ptr pMyOtherObject(new MyClass(2));
		pMyObject = pMyOtherObject;
	}
}

Oznacza to, że podczas przypisania pMyObject = pMyOtherObject; wskaźnik na obiekt nr 2 pamiętany w pMyOtherObject zostanie skopiowany do wskaźnika pMyObject i tym samym obiekt nr 1 zostanie usunięty i jego pamięć automatycznie zwolniona. Po tym fakcie następuje wydziedziczenie i wskaźnik automatyczny pMyOtherObject zostanie zresetowany (nie przechowuje już wskaźnika na obiekt nr 2). Mówimy o wydziedziczeniu na rzecz innego wskaźnika, gdyż prawa własności do obiektu są przenoszone, a nie współdzielone. Prawo własności oznacza, że właściciel (ang. owner) ma obowiązek zarządzać pamięcią obiektu w odróżnieniu od rodzica (ang. parent), który dostarcza swoim dzieciom tylko cech (tak jak w genetyce wygląd, osobowość, etc).

Jeśli zdefiniujemy dwie funkcje o sygnaturach jak niżej

void myFunc1(std::auto_ptr);
void myFunc2(std::auto_ptr&);

to wywołując myFunc1(pMyOtherObject) spowodujemy usunięcie obiektu nr 2 pod koniec działania tej funkcji ponieważ wykonane zostało kopiowanie automatycznego wskaźnika do zmiennej reprezentującej parametr funkcji, a parametry przestają istnieć gdy tylko konczy się wykonywać funkcja. Taka sytuacja nie zajdzie gdy wywołamy funkcję myFunc2(pMyOtherObject).

PAMIĘTAJ! Nie twórz parametrów typu std::auto_ptr, ale jako referencje do nich std::auto_ptr&.

Tags: ,

Leave a Reply