Sobes.tech
Middle

Co wiesz o inteligentnych wskaźnikach w C++?

sobes.tech AI

Odpowiedź od AI

Inteligentne wskaźniki to opakowania RAII nad zwykłymi (surowymi) wskaźnikami w C++, zapewniające automatyczne zarządzanie czasem życia przydzielonej pamięci, zapobiegając wyciekom.

Główne typy inteligentnych wskaźników w bibliotece standardowej C++:

  • std::unique_ptr
  • std::shared_ptr
  • std::weak_ptr

std::unique_ptr

  • Posiada zasób wyłącznie.
  • Nie może być kopiowany, ale może być przenoszony.
  • Używany do wyłącznego posiadania zasobu.
  • Koszty dodatkowe są minimalne, równoważne z surowym wskaźnikiem.
// przykład unique_ptr
#include <memory>
#include <iostream>

int main() {
    std::unique_ptr<int> ptr = std::make_unique<int>(10);
    std::cout << *ptr << std::endl; // 10
    // std::unique_ptr<int> ptr2 = ptr; // Błąd kompilacji: unique_ptr nie jest kopiowany
    std::unique_ptr<int> ptr3 = std::move(ptr); // Przeniesienie własności
    std::cout << *ptr3 << std::endl; // 10
    // std::cout << *ptr << std::endl; // Nieokreślone zachowanie: ptr jest teraz pusty
    return 0;
}

std::shared_ptr

  • Pozwala wielu wskaźnikom współdzielić własność zasobu.
  • Używa licznika odwołań. Pamięć jest zwalniana, gdy licznik osiąga zero.
  • Może być kopiowany. Każda kopia zwiększa licznik odwołań.
  • Ma wyższe koszty w porównaniu do unique_ptr ze względu na zarządzanie licznikiem.
// przykład shared_ptr
#include <memory>
#include <iostream>

int main() {
    std::shared_ptr<int> ptr1 = std::make_shared<int>(20);
    std::cout << *ptr1 << " (use_count: " << ptr1.use_count() << ")" << std::endl; // 20 (use_count: 1)
    std::shared_ptr<int> ptr2 = ptr1; // Kopiowanie, zwiększa use_count
    std::cout << *ptr1 << " (use_count: " << ptr1.use_count() << ")" << std::endl; // 20 (use_count: 2)
    std::shared_ptr<int> ptr3 = ptr1; // Kolejna kopia
    std::cout << *ptr1 << " (use_count: " << ptr1.use_count() << ")" << std::endl; // 20 (use_count: 3)
    return 0;
}

std::weak_ptr

  • Nie posiada własności zasobu i nie zwiększa licznika odwołań.
  • Używany do rozwiązania problemu cyklicznych odwołań między shared_ptr.
  • Umożliwia dostęp do zasobu wskazywanego przez shared_ptr, ale nie zapobiega jego usunięciu.
  • Aby uzyskać dostęp do zasobu, konieczne jest przekształcenie weak_ptr w shared_ptr za pomocą metody lock().
// przykład weak_ptr
#include <memory>
#include <iostream>

int main() {
    std::shared_ptr<int> shared_ptr_int = std::make_shared<int>(30);
    std::weak_ptr<int> weak_ptr_int = shared_ptr_int;

    if (auto locked_ptr = weak_ptr_int.lock()) {
        std::cout << *locked_ptr << std::endl; // 30
    } else {
        std::cout << "Zasób został zwolniony." << std::endl;
    }

    shared_ptr_int.reset(); // Zasób jest zwalniany

    if (auto locked_ptr = weak_ptr_int.lock()) {
        std::cout << *locked_ptr << std::endl;
    } else {
        std::cout << "Zasób został zwolniony." << std::endl; // Zasób został zwolniony.
    }
    return 0;
}

Używanie inteligentnych wskaźników jest dobrą praktyką we współczesnym C++, ponieważ znacznie zmniejsza prawdopodobieństwo błędów w zarządzaniu pamięcią.