Sobes.tech
Middle

Как работи броячът на препратки в std::shared_ptr?

sobes.tech AI

Отговор от AI

Броячът за връзки в std::shared_ptr се съхранява в отделен обект — блок за управление (control block).

Блокът за управление се създава:

  • При първото създаване на std::shared_ptr от указател.
  • При създаване на std::shared_ptr с потребителски изтривател или алокатор.
  • При използване на 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 << ") created\n";
    }
    ~MyObject() {
        std::cout << "MyObject(" << data << ") destroyed\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 << "Object data: " << locked_ptr->data << std::endl;
        }
        // силен брояч: 2, слаб брояч: 1
    } // ptr2 излиза извън обхвата си
    // силен брояч: 1, слаб брояч: 1

    // Обектът все още не е унищожен, тъй като силният брояч == 1

    {
        std::weak_ptr<MyObject> weak_ptr2 = ptr1;
        // силен брояч: 1, слаб брояч: 2
    } // weak_ptr2 излиза извън обхвата си
    // силен брояч: 1, слаб брояч: 1

} // ptr1 излиза извън обхвата си
// силен брояч: 0, слаб брояч: 1
// Обектът се унищожава, тъй като силният брояч става 0
// Блокът за управление остава, докато слабият брояч не стане 0