Inteligentne wskaźniki w C++
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 15 z 104.
Inteligentne wskaźniki to klasy opakowujące, które automatycznie zarządzają pamięcią sterty. Eliminują potrzebę ręcznego wywoływania delete, zwalniając pamięć, gdy wskaźnik wyjdzie poza zakres.
Do korzystania z inteligentnych wskaźników dołącz nagłówek <memory>.
unique_ptr — wyłączne posiadanie. Tylko jeden wskaźnik może w danej chwili posiadać zasób.
#include <memory>
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // Używaj jak zwykłego wskaźnika
// Nie można kopiować, ale można przenieść własność
std::unique_ptr<Player> player2 = std::move(player);
// player ma teraz wartość nullptrshared_ptr — współdzielone posiadanie. Wiele wskaźników może posiadać ten sam zasób. Pamięć jest zwalniana, gdy zostanie zniszczony ostatni właściciel.
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // Oba są właścicielami zasobu
std::cout << p1.use_count(); // Wypisuje 2weak_ptr — nieposiadający obserwator obiektu shared_ptr. Przydatny do przerywania cyklicznych odwołań.
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // Konwertuj na shared_ptr
locked->attack();
}Domyślnie używaj unique_ptr do wyłącznego posiadania. Używaj shared_ptr, gdy wiele obiektów musi współdzielić zasób. Używaj weak_ptr, aby obserwować bez przedłużania czasu życia.
Wybieraj make_unique i make_shared zamiast surowego new.
Wyzwanie
ŁatwyZbudujmy system udostępniania dokumentów, który demonstruje różne modele własności inteligentnych wskaźników. Zobaczysz, jak unique_ptr wymusza wyłączną własność, jak shared_ptr umożliwia współdzielenie własności przez wielu właścicieli oraz jak weak_ptr może obserwować obiekt bez wpływu na czas jego życia.
Utworzysz dwa pliki, aby uporządkować swój kod:
Document.h: Zdefiniuj klasęDocument, która reprezentuje udostępniany dokument. Powinna zawierać:- Prywatny atrybut
title(string) - Konstruktor, który przyjmuje tytuł i wyświetla
"Document created: <title>" - Destruktor, który wyświetla
"Document deleted: <title>" - Metodę
getTitle(), która zwraca tytuł
- Prywatny atrybut
main.cpp: Zademonstruj wszystkie trzy typy inteligentnych wskaźników, odczytując tytuł dokumentu z wejścia, a następnie:- Utwórz
unique_ptrwskazujący na dokument z podanym tytułem - Wyświetl
"Unique owner: <title>" - Przenieś własność na inny
unique_ptr, używającstd::move - Wyświetl
"Ownership transferred" - Utwórz
shared_ptrwskazujący na nowy dokument o tytule"SharedDoc" - Utwórz drugi
shared_ptrwskazujący na ten sam dokument - Wyświetl
"Shared owners: <count>", używającuse_count() - Utwórz
weak_ptrna podstawie jednego ze współdzielonych wskaźników - Użyj
lock(), aby uzyskać dostęp do dokumentu za pośrednictwem słabego wskaźnika, i wyświetl"Weak access: <title>"
- Utwórz
Pamiętaj, aby dołączyć <memory>, aby korzystać z inteligentnych wskaźników. Użyj std::make_unique i std::make_shared, aby utworzyć inteligentne wskaźniki. Komunikaty z destruktora pomogą ci zobaczyć, kiedy każdy dokument jest automatycznie usuwany po zakończeniu działania programu.
Dołącz plik nagłówkowy w main.cpp, używając #include "Document.h".
Spróbuj swoich sił
#include <iostream>
#include <memory>
#include <string>
#include "Document.h"
using namespace std;
int main() {
string inputTitle;
cin >> inputTitle;
// TODO: Utwórz unique_ptr do Document z inputTitle
// używając std::make_unique
// TODO: Wypisz "Unique owner: <title>"
// TODO: Przenieś własność do innego unique_ptr za pomocą std::move
// TODO: Wypisz "Ownership transferred"
// TODO: Utwórz shared_ptr do nowego Document o tytule "SharedDoc"
// używając std::make_shared
// TODO: Utwórz drugi shared_ptr wskazujący na ten sam dokument
// TODO: Wypisz "Shared owners: <count>" za pomocą use_count()
// TODO: Utwórz weak_ptr na podstawie jednego ze wskaźników współdzielonych
// TODO: Użyj lock(), aby uzyskać dostęp do dokumentu przez weak_ptr
// i wypisz "Weak access: <title>"
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online