Kihagyás

Smart pointerek

Smart pointerek

A pointerek használatának egy jelentős hátránya, hogy a memóriafelszabadítást a fejlesztőnek kell megoldania és egy - egy felszabadítás hiánya jelentős problémát okozhat. A smart pointerek használata ezt a felelősséget könnyíti, hiszen a memória foglalás és felszabadítás automatikusan történik.

A smart pointerek úgynevezett template osztályok. Számunkra ez azt jelenti, hogy használatukkor meg kell adni, hogy milyen típussal akarjuk használni a smart pointert '<' és '>' jelek között. Használatukhoz a memory header-t kell include-olni.

Pointerek használatakor a következő folyamatosak a lényegesebbek: - memóriafoglalás - másolás - memóriafelszabadítás

Smart pointerek használatával magát a pointert egy wrapper osztályba zárjuk és az adott osztály példányosításával hozunk létre pointert. Tehát ezek az objektumok nem dinamikus memóriát használó típusoknak értelmezendők (mint pl. Kurzus ahol a Hallgatókat tároltuk), hanem egy konkrét pointer típus helyett használandók. Az osztályoknak meg vannak valósítva a * és -> operátorai, így a szokványos pointer szintaxissal használhatjuk.

Memóriafoglalás: A smart pointer objektum példányosításakor lehetőség van memóriafoglalásra. (A smart pointerhez egyéb módon is foglalható memória, most csupán a konstruktoron keresztüli foglalást tárgyaljuk.) A memóriafoglalás után a smart pointer folyamatosan kezeli a memóriát.

Memóriafelszabadítás: Smart pointer objektumoknak is van élettartama, akárcsak egy általános objektumnak. Mikor egy objektum megszűnik, azt többé nem hivatkozhatjuk, így egy pointer esetében megszűnése előtt fel kell szabadítanunk a memóriát, különben meglévő referencia nélkül nem tudjuk, hogy melyik memóriaterületet kellene felszabadítani. Az objektum megszűnésekor lefut a destruktor, így a smart pointer automatikusan felszabadítja a foglalt memóriát.

Másolás: A dinamikus memória résznél a legtöbb feladatot a másolás okozott, hiszen deep-copy-t szerettünk volna elérni. Pointer másoláskor alap esetben nem foglalunk memóriát, hiszen csak értékeket állítunk be. (Ezért nagyon fontos, hogy nem dinamikus memóriát használó objektumról van szó, hanem pointer helyettesítőről.) A másolás viselkedése assignemnt operátor és másoló konstruktor használatával adható meg.

Unique pointer

Gyakran arra van szükség, hogy egy memóriaterületet csak egy változón keresztül érhessünk el. Ekkor std::unique_ptr típust használhatunk.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <memory>
struct Wrap {
  unsigned i = 0;
  Wrap( unsigned i ) : i( i ) {}
  Wrap() = default;
};

int main() {
  std::unique_ptr<Wrap> ptr( new Wrap( 2 ) ); // memoriafoglalas, Wrap-ban a 2-es ertek
  std::unique_ptr<Wrap[]> ptr2( std::make_unique<Wrap[]>( 10 ) ); // memoriafoglalas - 10 elem.

  // masolas
  // ptr2 = ptr; //forditasi hiba
  // masolni nem lehet

  // objektum megszunik.
  // memoriat nem kell kezzel felszabaditani.
  // objektum destruktor megoldja a felszabaditast.
}

A fenti példában is látható, hogy a másolás nem megengedett unique pointer esetében, ezzel is biztosítva, hogy egy memóriacím nem tartozik két objektumhoz (változó). A másolás azonban gyakran igény ekkor is, - legyen ez egy ideiglenes memória beállításakor -, így ezt is meg kell oldani. Ebben az esetben a feladatunk az eredeti foglalt memória felszabadítása és a memóriacím átállítása volt. Amire nem kellett figyelni, hogy a lemásolandó elemet többet ne tudjuk használni a memória elérésére.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <memory>
#include <iostream>
struct Wrap {
  unsigned i = 0;
  Wrap( unsigned i ) : i( i ) {}
  Wrap() = default;
};

int main() {
  std::unique_ptr<Wrap> ptr( new Wrap( 2 ) ); // memoriafoglalas, Wrap-ban a 2-es ertek
  std::unique_ptr<Wrap> ptr2( std::make_unique<Wrap>( 10 ) ); // memoriafoglalas - Wrap-ban a 10-es ertek.

  std::cout << (ptr2.get() == nullptr) << std::endl; // a tarolt pointer null-e? Nem.
  // masolas
  ptr = std::move( ptr2 );

  std::cout << (ptr2.get() == nullptr) << std::endl; // a tarolt pointer null-e? Igen, nem erjuk el a memoriat ezzel.

  // objektum megszunik.
  // memoriat nem kell kezzel felszabaditani.
  // objektum destruktor megoldja a felszabaditast.
}

Shared pointer

Shared pointer esetében azt várjuk, hogy a memóriacímet több objektum (változó) is hivatkozhassa. Ekkor a megoldandó feladat, hogy a memória csak akkor legyen felszabadítva, ha már egy objektum (változó) sem hivatkozza a memóriacímet. Shared pointer esetében ez megoldott, így ezzel nem kell foglalkoznunk.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <memory>
#include <iostream>
struct Wrap {
  unsigned i = 0;
  Wrap( unsigned i ) : i( i ) {}
  Wrap() = default;
};
void create_and_assign( std::shared_ptr<Wrap>& variable_to_assign ) {
  std::shared_ptr<Wrap> shared1( new Wrap( 10 ) ); // memoriafoglalas.
  variable_to_assign = shared1; // pointer masolas.
  // itt az első objektum megszunik.
}

int main() {
  std::shared_ptr<Wrap> shared2;
  create_and_assign( shared2 );
  // itt ha az elso objektum megszunese felszabaditotta volna a memoriat, hibat kapnank.
  std::cout << shared2->i << std::endl;
}

Utolsó frissítés: 2025-11-27
Létrehozva: 2025-11-27