Я начал изучать умные указатели C ++ 11, и я не вижу никакого полезного использования std::weak_ptr
. Может кто-нибудь сказать мне, когда std::weak_ptr
это полезно / необходимо?
268
Я начал изучать умные указатели C ++ 11, и я не вижу никакого полезного использования std::weak_ptr
. Может кто-нибудь сказать мне, когда std::weak_ptr
это полезно / необходимо?
Ответы:
Хорошим примером будет кеш.
Для объектов, к которым недавно обращались, вы хотите сохранить их в памяти, поэтому держите на них сильный указатель. Периодически вы сканируете кеш и решаете, к каким объектам недавно никто не обращался. Вам не нужно хранить их в памяти, поэтому вы избавляетесь от сильного указателя.
Но что, если этот объект используется и какой-то другой код содержит сильный указатель на него? Если кеш избавляется от своего единственного указателя на объект, он никогда не сможет найти его снова. Таким образом, кэш хранит слабый указатель на объекты, которые он должен найти, если они останутся в памяти.
Это именно то, что делает слабый указатель - он позволяет вам находить объект, если он все еще рядом, но не сохраняет его, если он больше не нужен.
источник
std::weak_ptr
это очень хороший способ решить висячий указатель . Просто используя необработанные указатели, невозможно узнать, были ли ссылочные данные освобождены или нет. Вместо этого, позволяяstd::shared_ptr
управлять данными и предоставляяstd::weak_ptr
пользователям данные, пользователи могут проверять достоверность данных, вызываяexpired()
илиlock()
.Вы не можете сделать это в
std::shared_ptr
одиночку, потому что всеstd::shared_ptr
экземпляры разделяют права собственности на данные, которые не удаляются до удаления всех экземпляровstd::shared_ptr
. Вот пример того, как проверить наличие висячих указателейlock()
:источник
std::weak_ptr::lock
создает новый,std::shared_ptr
который разделяет владение управляемым объектом.Еще один ответ, надеюсь, проще. (для коллег по Google)
Предположим, у вас есть
Team
иMember
объекты.Очевидно, это отношения:
Team
объект будет иметь указатели на негоMembers
. И вполне вероятно, что члены также будут иметь обратный указатель на свойTeam
объект.Тогда у вас есть цикл зависимости. Если вы используете
shared_ptr
, объекты больше не будут автоматически освобождаться, когда вы откажетесь от ссылки на них, потому что они ссылаются друг на друга циклически. Это утечка памяти.Вы ломаете это, используя
weak_ptr
. «Владелец» обычно использует,shared_ptr
а «принадлежащий» используетweak_ptr
родительский элемент и временно преобразует его вshared_ptr
когда ему требуется доступ к его родителю.Хранить слабый ptr:
затем используйте его при необходимости
источник
shared_ptr
том, чтобы делиться собственностью, поэтому никто не несет особой ответственности за освобождение памяти, она освобождается автоматически, когда она больше не используется. Если нет петли ... У вас может быть несколько команд, совместно использующих одного игрока (прошлые команды?). Если командный объект «владеет» участниками, то нет необходимости использовать ashared_ptr
для начала.shared_ptr
ссылаются ее «члены команды», когда она будет уничтожена? То, что вы описываете, это случай, когда петли нет.Вот один пример, данный мне @jleahy: Предположим, у вас есть набор задач, выполняемых асинхронно и управляемых с помощью
std::shared_ptr<Task>
. Возможно, вы захотите что-то делать с этими задачами периодически, поэтому событие таймера может пройти через astd::vector<std::weak_ptr<Task>>
и дать задачам что-то сделать. Однако одновременно задание может одновременно решить, что оно больше не нужно, и умереть. Таким образом, таймер может проверить, жива ли задача, путем создания общего указателя из слабого указателя и использования этого общего указателя, если он не равен нулю.источник
Они полезны с Boost.Asio, когда вам не гарантируется, что целевой объект все еще существует, когда вызывается асинхронный обработчик. Хитрость заключается в том, чтобы привязать объект
weak_ptr
к асинхронному объекту-обработчику, используяstd::bind
или лямбда-захват.Это вариант
self = shared_from_this()
идиомы, часто встречающийся в примерах Boost.Asio, где ожидающий асинхронный обработчик не продлевает время жизни целевого объекта, но все еще безопасен, если целевой объект удаляется.источник
this
self = shared_from_this()
идиомы, когда обработчик вызывает методы в том же классе.shared_ptr : содержит реальный объект.
weak_ptr : используется
lock
для соединения с реальным владельцем или возвращает NULL вshared_ptr
противном случае.Грубо говоря,
weak_ptr
роль похожа на роль агентства недвижимости . Без агентов, чтобы получить дом в аренду, нам, возможно, придется проверять случайные дома в городе. Агенты следят за тем, чтобы мы посещали только те дома, которые еще доступны и доступны для аренды.источник
weak_ptr
Также хорошо проверить правильность удаления объекта - особенно в модульных тестах. Типичный вариант использования может выглядеть следующим образом:источник
При использовании указателей важно понимать различные типы доступных указателей и когда имеет смысл использовать каждый из них. Существует четыре типа указателей в двух категориях:
SomeClass* ptrToSomeClass = new SomeClass();
]std::unique_ptr<SomeClass> uniquePtrToSomeClass ( new SomeClass() );
]
std::shared_ptr<SomeClass> sharedPtrToSomeClass ( new SomeClass() );
]
std::weak_ptr<SomeClass> weakPtrToSomeWeakOrSharedPtr ( weakOrSharedPtr );
]
Необработанные указатели (иногда называемые «устаревшими указателями» или «указателями C») обеспечивают поведение указателей «без костей» и являются распространенным источником ошибок и утечек памяти. Необработанные указатели не предоставляют средств для отслеживания владения ресурсом, и разработчики должны вызвать «delete» вручную, чтобы убедиться, что они не создают утечку памяти. Это становится трудным, если ресурс используется совместно, так как бывает сложно узнать, указывают ли какие-либо объекты на ресурс. По этим причинам следует избегать необработанных указателей и использовать их только в критически важных для кода разделах кода с ограниченной областью действия.
Уникальные указатели являются базовым интеллектуальным указателем, который «владеет» базовым необработанным указателем на ресурс и отвечает за вызов удаления и освобождение выделенной памяти, как только объект, которому «принадлежит» уникальный указатель, выходит из области видимости. Название «уникальный» относится к тому факту, что только один объект может «владеть» уникальным указателем в данный момент времени. Владение может быть передано другому объекту с помощью команды перемещения, но уникальный указатель никогда не может быть скопирован или передан. По этим причинам уникальные указатели являются хорошей альтернативой необработанным указателям в том случае, если указатель нужен только одному объекту в определенный момент времени, и это избавляет разработчика от необходимости освобождать память в конце жизненного цикла объекта-владельца.
Общие указатели - это еще один тип интеллектуальных указателей, которые похожи на уникальные указатели, но позволяют многим объектам владеть общим указателем. Как и уникальный указатель, совместно используемые указатели отвечают за освобождение выделенной памяти после того, как все объекты завершены, указывая на ресурс. Это достигается с помощью метода, называемого подсчетом ссылок. Каждый раз, когда новый объект становится владельцем общего указателя, счетчик ссылок увеличивается на единицу. Точно так же, когда объект выходит из области видимости или перестает указывать на ресурс, счетчик ссылок уменьшается на единицу. Когда счетчик ссылок достигает нуля, выделенная память освобождается. По этим причинам общие указатели являются очень мощным типом интеллектуального указателя, который следует использовать всякий раз, когда несколько объектов должны указывать на один и тот же ресурс.
Наконец, слабые указатели - это еще один тип интеллектуальных указателей, которые вместо прямого указания на ресурс указывают на другой указатель (слабый или общий). Слабые указатели не могут получить доступ к объекту напрямую, но они могут определить, существует ли объект до сих пор или срок его действия истек. Слабый указатель может быть временно преобразован в общий указатель для доступа к указанному объекту (при условии, что он все еще существует). Для иллюстрации рассмотрим следующий пример:
В этом примере у вас есть слабый указатель на собрание B. Вы не являетесь «владельцем» собрания B, поэтому оно может закончиться без вас, и вы не будете знать, закончилось ли оно или нет, пока вы не проверите. Если это не закончилось, вы можете присоединиться и участвовать, в противном случае вы не можете. Это отличается от наличия общего указателя на собрание B, поскольку в этом случае вы будете «владельцем» как на собрании A, так и на собрании B (участвуя в обоих одновременно).
В этом примере показано, как работает слабый указатель, и он полезен, когда объект должен быть сторонним наблюдателем , но не хочет ответственности за совместное владение. Это особенно полезно в сценарии, когда два объекта должны указывать друг на друга (круговая ссылка). При использовании общих указателей ни один объект не может быть освобожден, поскольку они все еще «сильно» указаны другим объектом. Когда один из указателей является слабым указателем, объект, содержащий слабый указатель, может при необходимости обращаться к другому объекту при условии, что он все еще существует.
источник
Помимо других уже упомянутых допустимых вариантов использования
std::weak_ptr
это замечательный инструмент в многопоточной среде, потому чтоstd::shared_ptr
в сочетании сstd::weak_ptr
безопасен от висячих указателей - в противоположностьstd::unique_ptr
в сочетании с необработанными указателямиstd::weak_ptr::lock()
является атомарной операцией (см. также О безопасности потоков в потоке weak_ptr )Рассмотрим задачу по одновременной загрузке всех изображений каталога (~ 10.000) в память (например, в виде кэша миниатюр). Очевидно, что лучший способ сделать это - поток управления, который обрабатывает и управляет изображениями, и несколько рабочих потоков, которые загружают изображения. Теперь это простая задача. Вот очень упрощенная реализация (и
join()
т. Д. Опущено, в реальной реализации потоки должны обрабатываться иначе)Но это становится намного сложнее, если вы хотите прервать загрузку изображений, например, потому что пользователь выбрал другой каталог. Или даже если вы хотите уничтожить менеджера.
Вам потребуется связь между потоками и остановка всех потоков загрузчика, прежде чем вы сможете изменить свое
m_imageDatas
поле. В противном случае загрузчики будут продолжать загрузку до тех пор, пока не будут выполнены все изображения, даже если они уже устарели. В упрощенном примере это не будет слишком сложно, но в реальной среде все может быть намного сложнее.Потоки, вероятно, будут частью пула потоков, используемого несколькими менеджерами, некоторые из которых останавливаются, а некоторые нет и т. Д. Простым параметром
imagesToLoad
будет заблокированная очередь, в которую эти менеджеры помещают свои запросы изображений из разных потоков управления. с читателями выскакивают запросы - в произвольном порядке - на другом конце. И поэтому общение становится сложным, медленным и подверженным ошибкам. Очень элегантный способ избежать какого-либо дополнительного общения в таких случаях - использоватьstd::shared_ptr
вместе сstd::weak_ptr
.Эта реализация почти так же проста, как и первая, не требует дополнительного взаимодействия потоков и может быть частью пула / очереди потоков в реальной реализации. Поскольку изображения с истекшим сроком пропускаются, а изображения с истекшим сроком действия обрабатываются, потоки никогда не должны были бы останавливаться во время нормальной работы. Вы всегда можете смело менять путь или уничтожать своих менеджеров, так как читатель fn проверяет, не истек ли срок действия указателя-владельца.
источник
http://en.cppreference.com/w/cpp/memory/weak_ptr std :: weak_ptr - это интеллектуальный указатель, содержащий несобственную («слабую») ссылку на объект, управляемый std :: shared_ptr. Он должен быть преобразован в std :: shared_ptr для доступа к ссылочному объекту.
std :: weak_ptr моделирует временное владение: когда доступ к объекту требуется только в том случае, если он существует, и он может быть в любой момент удален кем-то другим, std :: weak_ptr используется для отслеживания объекта и преобразуется в std: : shared_ptr для временного владения. Если исходный std :: shared_ptr уничтожается в это время, время жизни объекта увеличивается до тех пор, пока не будет уничтожен временный std :: shared_ptr.
Кроме того, std :: weak_ptr используется для разрыва циклических ссылок на std :: shared_ptr.
источник
Недостатком разделяемого указателя является то, что shared_pointer не может обработать зависимость родительско-дочернего цикла. Означает, если родительский класс использует объект дочернего класса, используя общий указатель, в том же файле, если дочерний класс использует объект родительского класса. Общий указатель не сможет уничтожить все объекты, даже общий указатель вообще не вызывает деструктор в сценарии зависимости цикла. в основном разделяемый указатель не поддерживает механизм подсчета ссылок.
Этот недостаток мы можем преодолеть с помощью weak_pointer.
источник
weak_ptr
справиться с циклической зависимостью без изменения логики программы в качестве замены для заменыshared_ptr
?» :-)Когда мы не хотим владеть объектом:
Пример:
В приведенном выше классе wPtr1 не является владельцем ресурса, на который указывает wPtr1. Если ресурс удален, срок действия wPtr1 истекает.
Чтобы избежать круговой зависимости:
Теперь, если мы сделаем shared_ptr классов B и A, use_count обоих указателей будет равен двум.
Когда shared_ptr выходит за пределы области действия, счетчик остается равным 1, и, следовательно, объекты A и B не удаляются.
вывод:
Как видно из вывода, указатели A и B никогда не удаляются и, следовательно, происходит утечка памяти.
Чтобы избежать такой проблемы, просто используйте weak_ptr в классе A вместо shared_ptr, что имеет больше смысла.
источник
Я вижу
std::weak_ptr<T>
в качестве ручки дляstd::shared_ptr<T>
: он позволяет мне получить,std::shared_ptr<T>
если он все еще существует, но он не продлит срок его службы. Есть несколько сценариев, когда такая точка зрения полезна:Другим важным сценарием является разрыв циклов в структурах данных.
Херб Саттер отлично говорит о том, как лучше всего использовать языковые функции (в данном случае умные указатели), чтобы обеспечить утечку свободы по умолчанию (имеется в виду: все защелкивается на месте по конструкции; вряд ли это можно испортить). Это нужно смотреть.
источник