23

Другими словами, как реализация отслеживает счет?Как работает подсчет ссылок для счетчика ссылок?

Поддерживается ли объект, похожий на карту, который доступен всем экземплярам shared_ptr, чей ключ является адресом и значением указателя, является число ссылок? Если я должен реализовать shared_ptr, это первая идея, которая приходит мне на ум.

Есть ли возможность утечки памяти в случае этих интеллектуальных указателей с подсчетом ссылок? Если да, то как я могу их избежать?

ответ

58

Я видел два разных неинтрузивные подходы к этому:

  1. смарт-указатель выделяет небольшой блок памяти, чтобы содержать счетчик ссылок. Каждая копия смарт-указателя затем получает указатель на фактический объект и указатель на счетчик ссылок.
  2. В дополнении к указателю на объект, каждого смарт-указателя содержит и предыдущий следующий указатель, тем самые образующих дважды связанный список смарта-указателей для конкретного объекта . Счетчик ссылок - , неявный в списке. Когда скопирован умный указатель , он добавляет себя в список . При уничтожении каждый умный указатель удаляет себя из списка . Если он последний в списке, он затем освобождает объект, на который ссылается .

Если вы идете here и прокрутите страницу вниз, есть отличная диаграмма, которая более четко объясняет эти методы.

+0

Это самый правильный ответ. –

+2

Метод связанных списков позволяет избежать дополнительного распределения, но очень сложно сделать «потокобезопасным» без глобального мьютекса. («поточно-безопасный», как в «как потокобезопасный, как необработанный указатель») – curiousguy

+2

Также, если вы используете 'make_shared', он также может избежать дополнительного распределения, помещая выделенный объект и счетчик экземпляров в один блок памяти. – Ferruccio

2

№ shared_ptr просто сохраните еще один указатель для подсчета ссылок.

Когда вы делаете копию объекта shared_ptr, он копирует указатель со счетчиком ссылок, увеличивает его и копирует указатель на содержащийся объект.

2

Создание утечки памяти с помощью интеллектуальных указателей с подсчетом ссылок очень просто. Просто создайте любую графоподобную структуру объектов, которая имеет цикл на графике. Объекты цикла будут препятствовать освобождению друг друга. Это невозможно решить автоматически - например, при создании списка с двойной связью вы должны заботиться о том, чтобы никогда не удалять более одного объекта за раз.

3

Каждый объект интеллектуального указателя содержит общий счетчик ссылок - по одному для каждого необработанного указателя.

Вы можете взглянуть на статью this. Эта реализация сохраняет их в отдельном объекте, который копируется вокруг. Вы также можете взглянуть на boost's documentation или взглянуть на Wikipedia article на умных указателях.

+0

-1. Все интеллектуальные указатели, которые ссылаются на один и тот же объект, должны * делиться * одним счетчиком ссылок. Объект, содержащий ссылочный счет, выделяется первым объектом интеллектуального указателя, и * указатели на него * (НЕ сам объект) копируются при копировании смарт-указателя. –

+0

Согласен с j_random_hacker. Счетчик уникален для каждого необработанного указателя и разделяется всеми shared_ptr, которые ссылаются на один и тот же необработанный указатель. Обычно он выделяется как отдельный блок памяти, поэтому smart_ptr имеет два внутренних ptrs, один для счетчика ссылок и другой для самого указателя. –

+0

-1 для статической переменной. Если вы не внедряете интеллектуальный указатель с подсчетом ссылок на объект singleton, вы не можете использовать статику для реализации подсчета ссылок. –

2

Насколько я помню, была проблема указателя подсчета ссылок, рассмотренного в главе «Эффективный С ++».

В принципе у вас есть класс указателей «светлый», содержащий указатель на класс, содержащий ссылку, которая знает ссылку на увеличение/уменьшение и уничтожает объект-указатель. Этот класс подсчета ссылок указывает на объект, на который нужно ссылаться.

2

Многие ответы касаются того, как хранится счетчик ссылок (он хранится в общей памяти для всех shared_ptr, которые содержат один и тот же внутренний указатель), но большинство уклоняются от проблемы утечек.

Самый простой способ утечки памяти со ссылкой подсчитанными указателями - создание циклов. В качестве примера гарантируется, что не будет удален двойной список, в котором все указатели shared_ptr с по меньшей мере двумя элементами. Даже если внешние указатели освобождаются, внутренние указатели будут по-прежнему считаться, а счетчик ссылок не достигнет 0. Это, по крайней мере, самая наивная реализация.

Простейшим решением проблемы цикла является смешивание shared_ptr (ссылки с подсчитанными указателями) со слабыми указателями, которые не разделяют права собственности на объект.

Общие указатели будут делиться как ресурсом (указателем), так и дополнительной информацией reference_count. Когда вы используете слабые указатели, счетчик ссылок удваивается: есть подсчет ссылок общего указателя и подсчет ссылок на слабые указатели. Ресурс выдается всякий раз, когда общий счетчик указателей достигает 0, но информация reference_count остается в живых до тех пор, пока не будет освобожден последний слабый указатель.

В двусвязном списке внешняя ссылка хранится в shared_ptr, а внутренние ссылки - только weak_ptr. Всякий раз, когда нет внешних ссылок (shared_ptr), элементы списка освобождаются, удаляя слабые ссылки. В конце все слабые ссылки были удалены, и последний слабый указатель на каждый ресурс освобождает информацию reference_count.

Это менее запутанно, чем кажется вышеприведенный текст ... Я попробую еще раз.

+2

Вы не пробовали позже. – xaxxon

Смежные вопросы