Как работает счетчик ссылок на подсчет ссылок?
Другими словами, как реализация отслеживает счет?
Существует ли поддерживаемый map-объект объект, доступный для всех экземпляров shared_ptr
, чей ключ является адресом указателя и значением является количество ссылок? Если я должен реализовать shared_ptr
, это первая идея, которая приходит мне на ум.
Есть ли возможность утечки памяти в случае этих интеллектуальных указателей с подсчетом ссылок? Если да, то как я могу их избежать?
Ответы
Ответ 1
Я видел два разных неинтрузивных подхода к этому:
- Умный указатель выделяет небольшой
блок памяти для
контрольный счетчик. Каждая копия
умный указатель затем получает
указатель на фактический объект и
указатель на счетчик ссылок.
- В дополнение к указателю на объект,
каждый умный указатель содержит
предыдущий и следующий указатель, тем самым
образуя двусвязный список
умные указатели на конкретный
объект. Счетчик ссылок
неявный в списке. Когда умный
указатель скопирован, он добавляет себя
список. При уничтожении каждый
умный указатель удаляется из
список. Если он последний в
список затем освобождает
ссылочный объект также.
Если вы перейдете здесь и прокрутите к нижней части, есть отличная диаграмма, которая объясняет эти методы гораздо более четко.
Ответ 2
Каждый объект интеллектуального указателя содержит общий счетчик ссылок - по одному для каждого необработанного указателя.
Вы можете взглянуть на эту статью. Эта реализация сохраняет их в отдельном объекте, который копируется вокруг. Вы также можете взглянуть на повысить документацию или взглянуть на Статья в Википедии о умных указателях.
Ответ 3
Нет. shared_ptr просто сохранит один дополнительный указатель для подсчета ссылок.
Когда вы делаете копию объекта shared_ptr, скопируйте указатель со списком ссылок, увеличьте его и скопируйте указатель на содержащийся объект.
Ответ 4
Создание утечки памяти с помощью интеллектуальных указателей с подсчетом ссылок очень просто. Просто создайте любую графоподобную структуру объектов, которая имеет цикл на графике. Объекты цикла будут препятствовать освобождению друг друга. Это невозможно решить автоматически - например, при создании списка с двойной связью вы должны заботиться о том, чтобы никогда не удалять более одного объекта за раз.
Ответ 5
Насколько я помню, была проблема указателя подсчета ссылок, рассмотренного в главе "Эффективный С++".
В принципе, у вас есть класс "светлый" указатель, содержащий указатель на класс, содержащий ссылку, которая знает, что нужно для увеличения/уменьшения ссылки и уничтожения объекта-указателя. Этот класс подсчета ссылок указывает на объект, на который нужно ссылаться.
Ответ 6
Многие ответы касаются того, как хранится счетчик ссылок (он хранится в общей памяти для всех shared_ptr, которые содержат один и тот же внутренний указатель), но большинство из них избегают проблемы утечек.
Самый простой способ утечки памяти со ссылками подсчитанных указателей - создание циклов. В качестве примера гарантируется, что не будет удален двойной список, в котором все указатели shared_ptr с по меньшей мере двумя элементами. Даже если внешние указатели освобождаются, внутренние указатели все равно будут подсчитываться, а счетчик ссылок не достигнет 0. Это, по крайней мере, самая наивная реализация.
Простейшим решением проблемы цикла является смешивание shared_ptr (ссылки с подсчитанными указателями) со слабыми указателями, которые не разделяют права собственности на объект.
Общие указатели будут делиться как ресурсом (указателем), так и дополнительной информацией reference_count. Когда вы используете слабые указатели, счетчик ссылок удваивается: есть подсчет ссылок общего указателя и подсчет ссылок на слабые указатели. Ресурс выделяется всякий раз, когда общий счетчик указателей достигает 0, но информация reference_count остается живой до тех пор, пока не будет освобожден последний слабый указатель.
В двусвязном списке внешняя ссылка хранится в shared_ptr, а внутренние ссылки - только weak_ptr. Всякий раз, когда нет внешних ссылок (shared_ptr), элементы списка освобождаются, удаляя слабые ссылки. В конце все слабые ссылки были удалены, и последний слабый указатель на каждый ресурс освобождает информацию reference_count.
Он менее запутан, чем кажется выше. Повторюсь позже.