Inteligentne wskaźniki w C++
Warunek wstępny: Wskaźniki w C++
Wskaźniki służą do uzyskiwania dostępu do zasobów zewnętrznych w stosunku do programu – takich jak pamięć sterty. Tak więc, aby uzyskać dostęp do pamięci sterty (jeśli cokolwiek zostanie utworzone w pamięci sterty), używane są wskaźniki. Uzyskując dostęp do dowolnego zasobu zewnętrznego, po prostu używamy kopii zasobu. Jeśli dokonamy w nim jakichkolwiek zmian, po prostu zmienimy to w skopiowanej wersji. Jeśli jednak użyjemy wskaźnika do zasobu, będziemy mogli zmienić oryginalny zasób.
Problemy ze zwykłymi wskaźnikami
Niektóre problemy z normalnymi wskaźnikami w C++ są następujące:
- Wycieki pamięci: Dzieje się tak, gdy pamięć jest wielokrotnie przydzielana przez program, ale nigdy nie jest zwalniana. Prowadzi to do nadmiernego zużycia pamięci i ostatecznie prowadzi do awarii systemu. Wiszące wskaźniki: Wiszący wskaźnik to wskaźnik, który pojawia się w momencie, gdy obiekt zostaje zwolniony z pamięci bez modyfikowania wartości wskaźnika. Wskaźniki typu Wild: Wskaźniki typu Wild to wskaźniki zadeklarowane i przydzielone do pamięci, ale wskaźnik nigdy nie jest inicjowany w celu wskazania jakiegokolwiek prawidłowego obiektu lub adresu. Niespójność danych: Niespójność danych występuje, gdy niektóre dane są przechowywane w pamięci, ale nie są aktualizowane w spójny sposób. Przepełnienie bufora: Gdy wskaźnik jest używany do zapisywania danych pod adresem pamięci znajdującym się poza przydzielonym blokiem pamięci. Prowadzi to do uszkodzenia danych, które mogą zostać wykorzystane przez złośliwych atakujących.
Przykład:
C++
// C++ program to demonstrate working of a Pointers> #include> using> namespace> std;> class> Rectangle {> private> :> > int> length;> > int> breadth;> };> void> fun()> {> > // By taking a pointer p and> > // dynamically creating object> > // of class rectangle> > Rectangle* p => new> Rectangle();> }> int> main()> {> > // Infinite Loop> > while> (1) {> > fun();> > }> }> |
Wyjście
Memory limit exceeded
Wyjaśnienie: W działaniu zabawa , tworzy wskaźnik wskazujący na Prostokąt obiekt. Obiekt Prostokąt zawiera dwie liczby całkowite, długość, I szerokość . Kiedy funkcja zabawa kończy się, p zostanie zniszczone, ponieważ jest to zmienna lokalna. Jednak zużyta pamięć nie zostanie zwolniona, ponieważ zapomnieliśmy jej użyć usuń p; na końcu funkcji. Oznacza to, że pamięć nie będzie wolna do wykorzystania przez inne zasoby. Ale nie potrzebujemy już zmiennej, potrzebujemy pamięci.
W działaniu, główny , zabawa wywoływana jest w nieskończonej pętli. Oznacza to, że będzie nadal tworzyć P . Będzie przydzielać coraz więcej pamięci, ale jej nie zwolni, ponieważ jej nie zwolniliśmy. Zmarnowanej pamięci nie można wykorzystać ponownie. Co jest wyciekiem pamięci. Cały sterta z tego powodu pamięć może stać się bezużyteczna.
Inteligentne wskaźniki
Jak już wiemy, nieświadome zwolnienie wskaźnika powoduje wyciek pamięci, który może doprowadzić do awarii programu. Języki Java, C# ma Mechanizmy zbierania śmieci aby inteligentnie zwolnić nieużywaną pamięć i wykorzystać ją ponownie. Programista nie musi się martwić o jakiekolwiek wycieki pamięci. C++ ma swój własny mechanizm Inteligentny wskaźnik . Kiedy obiekt zostaje zniszczony, uwalnia się także pamięć. Nie musimy więc go usuwać, ponieważ poradzi sobie z tym Smart Pointer.
A Inteligentny wskaźnik jest klasą opakowania nad wskaźnikiem z operatorem like * I -> przeciążony. Obiekty klasy inteligentnych wskaźników wyglądają jak zwykłe wskaźniki. Ale w przeciwieństwie do Normalne wskaźniki, może zwolnić i zwolnić pamięć zniszczonego obiektu.
Pomysł jest taki, aby wziąć udział w zajęciach ze wskaźnikiem, niszczyciel, i przeciążone operatory, takie jak * I -> . Ponieważ destruktor jest wywoływany automatycznie, gdy obiekt wykracza poza zakres, dynamicznie przydzielana pamięć zostanie automatycznie usunięta (lub można zmniejszyć liczbę odwołań).
Przykład:
C++
// C++ program to demonstrate the working of Smart Pointer> #include> using> namespace> std;> class> SmartPtr {> > int> * ptr;> // Actual pointer> public> :> > // Constructor: Refer> > // techcodeview.com for use of> > // explicit keyword> > explicit> SmartPtr(> int> * p = NULL) { ptr = p; }> > // Destructor> > ~SmartPtr() {> delete> (ptr); }> > // Overloading dereferencing operator> > int> & operator*() {> return> *ptr; }> };> int> main()> {> > SmartPtr ptr(> new> int> ());> > *ptr = 20;> > cout < < *ptr;> > // We don't need to call delete ptr: when the object> > // ptr goes out of scope, the destructor for it is> > // automatically called and destructor does delete ptr.> > return> 0;> }> |
Wyjście
20
Różnica między wskaźnikami a inteligentnymi wskaźnikami
| Wskaźnik | Inteligentny wskaźnik |
|---|---|
| Wskaźnik to zmienna przechowująca adres pamięci, a także informacje o typie danych o tej lokalizacji pamięci. Wskaźnik to zmienna wskazująca na coś w pamięci. | Jest to obiekt alokowany na stosie, zawijający wskaźniki. Inteligentne wskaźniki, mówiąc najprościej, to klasy zawijające wskaźnik lub wskaźniki o określonym zasięgu. |
| Nie ulega zniszczeniu w żadnej formie, gdy wychodzi poza swój zakres | Niszczy się sam, gdy wychodzi poza swój zasięg |
| Wskaźniki nie są tak wydajne, ponieważ nie obsługują żadnej innej funkcji. | Inteligentne wskaźniki są bardziej wydajne, ponieważ mają dodatkową funkcję zarządzania pamięcią. |
| Są bardzo pracochłonni/manualni. | Mają one charakter automatyczny/wstępnie zaprogramowany. |
Notatka: To działa tylko dla wew . Zatem będziemy musieli utworzyć inteligentny wskaźnik dla każdego obiektu? NIE , istnieje rozwiązanie, Szablon . W poniższym kodzie, jak widać T może być dowolnego typu.
Przykład:
C++
// C++ program to demonstrate the working of Template and> // overcome the issues which we are having with pointers> #include> using> namespace> std;> // A generic smart pointer class> template> <> class> T>> class> SmartPtr {> > T* ptr;> // Actual pointer> public> :> > // Constructor> > explicit> SmartPtr(T* p = NULL) { ptr = p; }> > // Destructor> > ~SmartPtr() {> delete> (ptr); }> > // Overloading dereferencing operator> > T& operator*() {> return> *ptr; }> > // Overloading arrow operator so that> > // members of T can be accessed> > // like a pointer (useful if T represents> > // a class or struct or union type)> > T* operator->() {> return> ptr; }> };> int> main()> {> > SmartPtr <> int> >ptr(> new> int> ());> > *ptr = 20;> > cout < < *ptr;> > return> 0;> }> |
Wyjście
20
Notatka: Inteligentne wskaźniki przydają się także w zarządzaniu zasobami, takimi jak uchwyty plików czy gniazda sieciowe.
Rodzaje inteligentnych wskaźników
Biblioteki C++ zapewniają implementacje inteligentnych wskaźników następujących typów:
- auto_ptr
- unikalny_ptr
- wspólna_ptr
- słaby_ptr
auto_ptr
Używając auto_ptr, możesz zarządzać obiektami uzyskanymi z nowych wyrażeń i usuwać je, gdy sam auto_ptr zostanie zniszczony. Kiedy obiekt jest opisany poprzez auto_ptr, przechowuje wskaźnik do pojedynczego przydzielonego obiektu.
Notatka: Ten szablon klasy jest przestarzały od wersji C++ 11. Unique_ptr to nowy obiekt o podobnej funkcjonalności, ale z poprawionym bezpieczeństwem.
unikalny_ptr
unikalny_ptr przechowuje tylko jeden wskaźnik. Możemy przypisać inny obiekt usuwając bieżący obiekt ze wskaźnika.
Przykład:
C++
// C++ program to demonstrate the working of unique_ptr> // Here we are showing the unique_pointer is pointing to P1.> // But, then we remove P1 and assign P2 so the pointer now> // points to P2.> #include> using> namespace> std;> // Dynamic Memory management library> #include> class> Rectangle {> > int> length;> > int> breadth;> public> :> > Rectangle(> int> l,> int> b)> > {> > length = l;> > breadth = b;> > }> > int> area() {> return> length * breadth; }> };> int> main()> {> // --/ Smart Pointer> > unique_ptr P1(> new> Rectangle(10, 5));> > cout // This'll print 50 // unique_ptr P2(P1); unique_ptr P2; P2 = move(P1); // This'll print 50 cout // cout return 0; }> |
Wyjście
50 50
wspólna_ptr
Używając wspólna_ptr więcej niż jeden wskaźnik może wskazywać na ten jeden obiekt na raz i będzie on zachowywał a Licznik referencji używając liczba_użyć() metoda.
C++
// C++ program to demonstrate the working of shared_ptr> // Here both smart pointer P1 and P2 are pointing to the> // object Addition to which they both maintain a reference> // of the object> #include> using> namespace> std;> // Dynamic Memory management library> #include> class> Rectangle {> > int> length;> > int> breadth;> public> :> > Rectangle(> int> l,> int> b)> > {> > length = l;> > breadth = b;> > }> > int> area() {> return> length * breadth; }> };> int> main()> {> > //---/ Smart Pointer> > shared_ptr P1(> new> Rectangle(10, 5));> > // This'll print 50> > cout shared_ptr P2; P2 = P1; // This'll print 50 cout // This'll now not give an error, cout // This'll also print 50 now // This'll print 2 as Reference Counter is 2 cout < < P1.use_count() < < endl; return 0; }> |
Wyjście
50 50 50 2
słaby_ptr
Weak_ptr to inteligentny wskaźnik, który przechowuje odwołanie do obiektu niebędącego właścicielem. Jest znacznie bardziej podobny doshared_ptr, z tą różnicą, że nie obsługuje pliku a Licznik referencji . W tym przypadku wskaźnik nie będzie miał twierdzy na obiekcie. Powodem jest to, że jeśli założymy, że wskaźniki trzymają obiekt i proszą o inne obiekty, mogą utworzyć a Impas.
C++
// C++ program to demonstrate the working of weak_ptr> // Here both smart pointer P1 and P2 are pointing to the> // object Addition to which they both does not maintain> // a reference of the object> #include> using> namespace> std;> // Dynamic Memory management library> #include> class> Rectangle {> > int> length;> > int> breadth;> public> :> > Rectangle(> int> l,> int> b)> > {> > length = l;> > breadth = b;> > }> > int> area() {> return> length * breadth; }> };> int> main()> {> > //---/ Smart Pointer> > shared_ptr P1(> new> Rectangle(10, 5));> > > // create weak ptr> > weak_ptr P2 (P1);> > > // This'll print 50> > cout // This'll print 1 as Reference Counter is 1 cout < < P1.use_count() < < endl; return 0; }> |
Wyjście
50 1
Biblioteki C++ zapewniają implementacje inteligentnych wskaźników w postaci auto_ptr, Unique_ptr, wspólna_ptr i słabe_ptr