Sobes.tech
Middle

Πώς λειτουργεί ο μετρητής αναφορών στο std::shared_ptr;

sobes.tech AI

Απάντηση από AI

Ο μετρητής αναφορών στο std::shared_ptr αποθηκεύεται σε ένα ξεχωριστό αντικείμενο: το μπλοκ ελέγχου (control block).

Το μπλοκ ελέγχου δημιουργείται:

  • Κατά την πρώτη δημιουργία ενός std::shared_ptr από έναν δείκτη.
  • Κατά τη δημιουργία ενός std::shared_ptr με προσαρμοσμένο διαγραφέα ή allocator.
  • Κατά τη χρήση του std::make_shared ή std::allocate_shared.

Αυτό το μπλοκ ελέγχου περιέχει τουλάχιστον δύο μετρητές:

  1. Μετρητής ισχυρών αναφορών (strong count): Αυξάνεται κατά τη δημιουργία ή αντιγραφή ενός std::shared_ptr. Μειώνεται κατά την καταστροφή ενός std::shared_ptr. Όταν αυτός ο μετρητής φτάσει το μηδέν, το διαχειριζόμενο αντικείμενο απελευθερώνεται.
  2. Μετρητής αδύναμων αναφορών (weak count): Αυξάνεται κατά τη δημιουργία ενός std::weak_ptr από ένα std::shared_ptr. Μειώνεται κατά την καταστροφή ενός std::weak_ptr. Το μπλοκ ελέγχου απελευθερώνεται όταν και οι δύο μετρητές — ισχυρός και αδύναμος — φτάσουν το μηδέν.

Η χρήση του std::make_shared προτιμάται έναντι της άμεσης δημιουργίας με new, καθώς μπορεί να κατανεμηθεί ο αντικείμενο και το μπλοκ ελέγχου σε ένα μόνο μπλοκ μνήμης, βελτιώνοντας την απόδοση και μειώνοντας τον κατακερματισμό.

Οι μετρητές είναι ατομικοί, καθιστώντας το std::shared_ptr ασφαλές για χρήση σε περιβάλλοντα πολλαπλών νημάτων, αν και η πρόσβαση στο διαχειριζόμενο αντικείμενο δεν συγχρονίζεται από προεπιλογή.

Παράδειγμα:

#include <iostream>
#include <memory>

struct MyObject {
    int data;
    MyObject(int d) : data(d) {
        std::cout << "MyObject(" << data << ") δημιουργήθηκε\n";
    }
    ~MyObject() {
        std::cout << "MyObject(" << data << ") καταστράφηκε\n";
    }
};

int main() {
    std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
    // ισχυρός μετρητής: 1, αδύναμος μετρητής: 0

    {
        std::shared_ptr<MyObject> ptr2 = ptr1;
        // ισχυρός μετρητής: 2, αδύναμος μετρητής: 0

        std::weak_ptr<MyObject> weak_ptr = ptr1;
        // ισχυρός μετρητής: 2, αδύναμος μετρητής: 1

        if (auto locked_ptr = weak_ptr.lock()) {
            // ισχυρός μετρητής: 3, αδύναμος μετρητής: 1 (πρόσκαιρα μέσα σε lock)
            std::cout << "Δεδομένα αντικειμένου: " << locked_ptr->data << std::endl;
        }
        // ισχυρός μετρητής: 2, αδύναμος μετρητής: 1
    } // το ptr2 βγαίνει από το scope
    // ισχυρός μετρητής: 1, αδύναμος μετρητής: 1

    // Το αντικείμενο δεν έχει ακόμη καταστραφεί, καθώς ο ισχυρός μετρητής == 1

    {
        std::weak_ptr<MyObject> weak_ptr2 = ptr1;
        // ισχυρός μετρητής: 1, αδύναμος μετρητής: 2
    } // το weak_ptr2 βγαίνει από το scope
    // ισχυρός μετρητής: 1, αδύναμος μετρητής: 1

} // το ptr1 βγαίνει από το scope
// ισχυρός μετρητής: 0, αδύναμος μετρητής: 1
// Το αντικείμενο καταστρέφεται, καθώς ο ισχυρός μετρητής έφτασε το 0
// Το μπλοκ ελέγχου παραμένει μέχρι ο αδύναμος μετρητής να φτάσει το 0