Как работает умный указатель подсчета ссылок? - PullRequest
27 голосов
/ 07 апреля 2009

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

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

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

Ответы [ 6 ]

64 голосов
/ 07 апреля 2009

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

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

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

4 голосов
/ 07 апреля 2009

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

3 голосов
/ 07 апреля 2009

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

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

2 голосов
/ 07 апреля 2009

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

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

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

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

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

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

2 голосов
/ 07 апреля 2009

Насколько я помню, проблема с указателем подсчета ссылок рассматривалась в главе Effective C ++.

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

2 голосов
/ 07 апреля 2009

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

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

...