Что такое счетчик ссылок (reference counting) в C++?

«Что такое счетчик ссылок (reference counting) в C++?» — вопрос из категории Управление памятью, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Счетчик ссылок — это техника управления временем жизни объекта, при которой сам объект (или связанный с ним управляющий блок) хранит целочисленный счетчик активных ссылок на себя. При создании новой ссылки счетчик увеличивается, при уничтожении ссылки — уменьшается. Когда счетчик достигает нуля, память объекта освобождается.

Реализация в стандартной библиотеке: В C++ эта концепция лежит в основе умного указателя std::shared_ptr.

#include <iostream>
#include <memory>

class Resource {
public:
    Resource() { std::cout << "Resource acquiredn"; }
    ~Resource() { std::cout << "Resource destroyedn"; }
    void use() { std::cout << "Resource usedn"; }
};

int main() {
    std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>(); // Счетчик = 1
    {
        std::shared_ptr<Resource> ptr2 = ptr1; // Счетчик увеличивается до 2
        ptr2->use();
    } // ptr2 выходит из области видимости, счетчик уменьшается до 1
    ptr1->use();
    // ptr1 выходит из области видимости, счетчик становится 0, ресурс уничтожается
    return 0;
}

Вывод:

Resource acquired
Resource used
Resource used
Resource destroyed

Преимущества и недостатки:

  • Плюсы: Предсказуемое время освобождения памяти (детерминированное), отсутствие пауз на сборку мусора.
  • Минусы:
    1. Циклические ссылки: Если два shared_ptr ссылаются друг на друга, их счетчики никогда не обнулятся, что приводит к утечке памяти. Решение — использование std::weak_ptr для разрыва циклов.
    2. Накладные расходы: Необходимость атомарного увеличения/уменьшения счетчика в многопоточных сценариях.
    3. Дополнительная память: Хранение счетчика (обычно в управляющем блоке).