Когда полезен std :: weak_ptr?

268

Я начал изучать умные указатели C ++ 11, и я не вижу никакого полезного использования std::weak_ptr. Может кто-нибудь сказать мне, когда std::weak_ptrэто полезно / необходимо?

ARTM
источник

Ответы:

232

Хорошим примером будет кеш.

Для объектов, к которым недавно обращались, вы хотите сохранить их в памяти, поэтому держите на них сильный указатель. Периодически вы сканируете кеш и решаете, к каким объектам недавно никто не обращался. Вам не нужно хранить их в памяти, поэтому вы избавляетесь от сильного указателя.

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

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

Дэвид Шварц
источник
8
Таким образом, std :: wake_ptr может указывать только там, где указывает другой указатель, и указывает на nullptr, когда указанный объект удален / не указан другими указателями?
27
@RM: В основном да. Если у вас слабый указатель, вы можете попытаться повысить его до сильного указателя. Если этот объект все еще существует (поскольку по-прежнему существует хотя бы один сильный указатель на него), эта операция завершается успешно и дает вам сильный указатель на него. Если этот объект не существует (потому что все сильные указатели исчезли), то эта операция не выполняется (и обычно вы реагируете, выбрасывая слабый указатель).
Дэвид Шварц
12
В то время как сильный указатель поддерживает объект живым, strong_ptr может смотреть на него ... без ущерба для времени жизни объекта.
The Vivandiere
3
Другой пример, который я использовал, по крайней мере, несколько раз, - при реализации наблюдателей иногда бывает удобно, чтобы субъект поддерживал список слабых указателей и выполнял собственную очистку списка. Это экономит немного усилий при явном удалении наблюдателей при их удалении, и, что более важно, вам не нужно иметь информацию о предметах, доступных при уничтожении наблюдателей, что, как правило, значительно упрощает ситуацию.
Джейсон С
3
Подождите, что не так с кешем, содержащим shared_ptr и просто удаляющим его из списка, когда он должен быть очищен из памяти? Любой пользователь все равно будет хранить shared_ptr, и кэшированный ресурс будет очищен, как только все пользователи покончат с ним.
rubenvb
300

std::weak_ptrэто очень хороший способ решить висячий указатель . Просто используя необработанные указатели, невозможно узнать, были ли ссылочные данные освобождены или нет. Вместо этого, позволяя std::shared_ptrуправлять данными и предоставляя std::weak_ptrпользователям данные, пользователи могут проверять достоверность данных, вызывая expired()или lock().

Вы не можете сделать это в std::shared_ptrодиночку, потому что все std::shared_ptrэкземпляры разделяют права собственности на данные, которые не удаляются до удаления всех экземпляров std::shared_ptr. Вот пример того, как проверить наличие висячих указателей lock():

#include <iostream>
#include <memory>

int main()
{
    // OLD, problem with dangling pointer
    // PROBLEM: ref will point to undefined data!

    int* ptr = new int(10);
    int* ref = ptr;
    delete ptr;

    // NEW
    // SOLUTION: check expired() or lock() to determine if pointer is valid

    // empty definition
    std::shared_ptr<int> sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr<int> weak1 = sptr;

    // deletes managed object, acquires new pointer
    sptr.reset(new int);
    *sptr = 5;

    // get pointer to new data without taking ownership
    std::weak_ptr<int> weak2 = sptr;

    // weak1 is expired!
    if(auto tmp = weak1.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak1 is expired\n";

    // weak2 points to new data (5)
    if(auto tmp = weak2.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak2 is expired\n";
}
sunefred
источник
1
Хорошо, как если бы вы локально установили (собственный) указатель на ноль (удалить память), все остальные (слабые) указатели на ту же память также будут установлены на ноль
Pat-Laugh
std::weak_ptr::lockсоздает новый, std::shared_ptrкоторый разделяет владение управляемым объектом.
Сахиб Яр
129

Еще один ответ, надеюсь, проще. (для коллег по Google)

Предположим, у вас есть Teamи Memberобъекты.

Очевидно, это отношения: Teamобъект будет иметь указатели на него Members. И вполне вероятно, что члены также будут иметь обратный указатель на свой Teamобъект.

Тогда у вас есть цикл зависимости. Если вы используетеshared_ptr , объекты больше не будут автоматически освобождаться, когда вы откажетесь от ссылки на них, потому что они ссылаются друг на друга циклически. Это утечка памяти.

Вы ломаете это, используя weak_ptr. «Владелец» обычно использует, shared_ptrа «принадлежащий» использует weak_ptrродительский элемент и временно преобразует его вshared_ptr когда ему требуется доступ к его родителю.

Хранить слабый ptr:

weak_ptr<Parent> parentWeakPtr_ = parentSharedPtr; // automatic conversion to weak from shared

затем используйте его при необходимости

shared_ptr<Parent> tempParentSharedPtr = parentWeakPtr_.lock(); // on the stack, from the weak ptr
if( !tempParentSharedPtr ) {
  // yes, it may fail if the parent was freed since we stored weak_ptr
} else {
  // do stuff
}
// tempParentSharedPtr is released when it goes out of scope
Offirmo
источник
1
Как это утечка памяти? Если команда уничтожена, она уничтожит своих членов, таким образом, счетчик ссылок shared_ptr будет равен 0 и также уничтожен?
Пол
4
Команда @paulm не будет уничтожать "своих" участников. Все дело в shared_ptrтом, чтобы делиться собственностью, поэтому никто не несет особой ответственности за освобождение памяти, она освобождается автоматически, когда она больше не используется. Если нет петли ... У вас может быть несколько команд, совместно использующих одного игрока (прошлые команды?). Если командный объект «владеет» участниками, то нет необходимости использовать a shared_ptrдля начала.
Offirmo
1
Он не уничтожит их, но его shared_ptr выйдет из области видимости, уменьшит значение use_count, поэтому на этом этапе значение use_count равно 0, и поэтому shared_ptr удалит то, на что он указывает?
Пол
2
@paulm Вы правы. Но поскольку в этом примере на команду также shared_ptrссылаются ее «члены команды», когда она будет уничтожена? То, что вы описываете, это случай, когда петли нет.
Offirmo
14
Это не так плохо, я бы подумал. Если участник может принадлежать ко многим командам, использование ссылки не сработает.
Мазёд
22

Вот один пример, данный мне @jleahy: Предположим, у вас есть набор задач, выполняемых асинхронно и управляемых с помощью std::shared_ptr<Task>. Возможно, вы захотите что-то делать с этими задачами периодически, поэтому событие таймера может пройти через a std::vector<std::weak_ptr<Task>>и дать задачам что-то сделать. Однако одновременно задание может одновременно решить, что оно больше не нужно, и умереть. Таким образом, таймер может проверить, жива ли задача, путем создания общего указателя из слабого указателя и использования этого общего указателя, если он не равен нулю.

Керрек С.Б.
источник
4
Звучит как хороший пример, но не могли бы вы рассказать о нем подробнее? Я думаю, что когда задача завершена, она должна быть удалена из std :: vector <std :: weak_ptr <Task >> без периодической проверки. Поэтому не уверен, что std :: vector <std :: weak_ptr <>> очень полезен здесь.
Gob00st
Аналогичный комментарий с очередями: скажем, у вас есть объекты, и вы ставите их в очередь для какого-то ресурса, объекты могут быть удалены во время ожидания. Таким образом, если вы ставите в очередь weak_ptrs, вам не нужно беспокоиться об удалении записей из этой очереди. Weak_ptrs будет признан недействительным, а затем отброшен при нахождении.
zzz777
1
@ zzz777: логика, которая делает недействительными объекты, может даже не знать о существовании очереди или вектора наблюдателей. Таким образом, наблюдатель выполняет отдельный цикл над слабыми указателями, воздействуя на те, которые еще живы, и удаляя мертвых из контейнера ...
Kerrek SB
1
@KerekSB: да, и в случае очереди вам даже не нужен отдельный цикл - тогда ресурс становится доступным, и вы сбрасываете срок действия слабых_символов (если есть), пока не получите действительный (если есть).
zzz777
Вы также можете удалить потоки из коллекции, но это создаст зависимость и потребует блокировки.
любопытный парень
16

Они полезны с Boost.Asio, когда вам не гарантируется, что целевой объект все еще существует, когда вызывается асинхронный обработчик. Хитрость заключается в том, чтобы привязать объект weak_ptrк асинхронному объекту-обработчику, используя std::bindили лямбда-захват.

void MyClass::startTimer()
{
    std::weak_ptr<MyClass> weak = shared_from_this();
    timer_.async_wait( [weak](const boost::system::error_code& ec)
    {
        auto self = weak.lock();
        if (self)
        {
            self->handleTimeout();
        }
        else
        {
            std::cout << "Target object no longer exists!\n";
        }
    } );
}

Это вариант self = shared_from_this()идиомы, часто встречающийся в примерах Boost.Asio, где ожидающий асинхронный обработчик не продлевает время жизни целевого объекта, но все еще безопасен, если целевой объект удаляется.

Эмиль Кормье
источник
Почему это заняло так много времени, чтобы найти этот ответ ... PS Вы не используете свой захватthis
Орвеллофил
@ Орвеллофил исправлен. Сила привычки при использовании self = shared_from_this()идиомы, когда обработчик вызывает методы в том же классе.
Эмиль Кормье
16

shared_ptr : содержит реальный объект.

weak_ptr : используется lockдля соединения с реальным владельцем или возвращает NULL в shared_ptrпротивном случае.

слабый взгляд

Грубо говоря, weak_ptrроль похожа на роль агентства недвижимости . Без агентов, чтобы получить дом в аренду, нам, возможно, придется проверять случайные дома в городе. Агенты следят за тем, чтобы мы посещали только те дома, которые еще доступны и доступны для аренды.

Саурав Саху
источник
14

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

std::weak_ptr<X> weak_x{ shared_x };
shared_x.reset();
BOOST_CHECK(weak_x.lock());
... //do something that should remove all other copies of shared_x and hence destroy x
BOOST_CHECK(!weak_x.lock());
печенье
источник
13

При использовании указателей важно понимать различные типы доступных указателей и когда имеет смысл использовать каждый из них. Существует четыре типа указателей в двух категориях:

  • Сырые указатели:
    • Сырой указатель [то есть 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 все еще продолжается после окончания собрания A, вы присоединитесь
  • Возможны следующие два сценария:
    • Совещание A заканчивается, а собрание B все еще продолжается, поэтому вы присоединяетесь
    • Совещание A заканчивается, и собрание B также завершено, поэтому вы не можете присоединиться

В этом примере у вас есть слабый указатель на собрание B. Вы не являетесь «владельцем» собрания B, поэтому оно может закончиться без вас, и вы не будете знать, закончилось ли оно или нет, пока вы не проверите. Если это не закончилось, вы можете присоединиться и участвовать, в противном случае вы не можете. Это отличается от наличия общего указателя на собрание B, поскольку в этом случае вы будете «владельцем» как на собрании A, так и на собрании B (участвуя в обоих одновременно).

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

Джереми
источник
6

Помимо других уже упомянутых допустимых вариантов использования std::weak_ptrэто замечательный инструмент в многопоточной среде, потому что

  • Он не владеет объектом и поэтому не может помешать удалению в другом потоке.
  • std::shared_ptrв сочетании с std::weak_ptrбезопасен от висячих указателей - в противоположность std::unique_ptrв сочетании с необработанными указателями
  • std::weak_ptr::lock()является атомарной операцией (см. также О безопасности потоков в потоке weak_ptr )

Рассмотрим задачу по одновременной загрузке всех изображений каталога (~ 10.000) в память (например, в виде кэша миниатюр). Очевидно, что лучший способ сделать это - поток управления, который обрабатывает и управляет изображениями, и несколько рабочих потоков, которые загружают изображения. Теперь это простая задача. Вот очень упрощенная реализация (и join()т. Д. Опущено, в реальной реализации потоки должны обрабатываться иначе)

// a simplified class to hold the thumbnail and data
struct ImageData {
  std::string path;
  std::unique_ptr<YourFavoriteImageLibData> image;
};

// a simplified reader fn
void read( std::vector<std::shared_ptr<ImageData>> imagesToLoad ) {
   for( auto& imageData : imagesToLoad )
     imageData->image = YourFavoriteImageLib::load( imageData->path );
}

// a simplified manager
class Manager {
   std::vector<std::shared_ptr<ImageData>> m_imageDatas;
   std::vector<std::unique_ptr<std::thread>> m_threads;
public:
   void load( const std::string& folderPath ) {
      std::vector<std::string> imagePaths = readFolder( folderPath );
      m_imageDatas = createImageDatas( imagePaths );
      const unsigned numThreads = std::thread::hardware_concurrency();
      std::vector<std::vector<std::shared_ptr<ImageData>>> splitDatas = 
        splitImageDatas( m_imageDatas, numThreads );
      for( auto& dataRangeToLoad : splitDatas )
        m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
   }
};

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

Вам потребуется связь между потоками и остановка всех потоков загрузчика, прежде чем вы сможете изменить свое m_imageDatasполе. В противном случае загрузчики будут продолжать загрузку до тех пор, пока не будут выполнены все изображения, даже если они уже устарели. В упрощенном примере это не будет слишком сложно, но в реальной среде все может быть намного сложнее.

Потоки, вероятно, будут частью пула потоков, используемого несколькими менеджерами, некоторые из которых останавливаются, а некоторые нет и т. Д. Простым параметром imagesToLoadбудет заблокированная очередь, в которую эти менеджеры помещают свои запросы изображений из разных потоков управления. с читателями выскакивают запросы - в произвольном порядке - на другом конце. И поэтому общение становится сложным, медленным и подверженным ошибкам. Очень элегантный способ избежать какого-либо дополнительного общения в таких случаях - использовать std::shared_ptrвместе с std::weak_ptr.

// a simplified reader fn
void read( std::vector<std::weak_ptr<ImageData>> imagesToLoad ) {
   for( auto& imageDataWeak : imagesToLoad ) {
     std::shared_ptr<ImageData> imageData = imageDataWeak.lock();
     if( !imageData )
        continue;
     imageData->image = YourFavoriteImageLib::load( imageData->path );
   }
}

// a simplified manager
class Manager {
   std::vector<std::shared_ptr<ImageData>> m_imageDatas;
   std::vector<std::unique_ptr<std::thread>> m_threads;
public:
   void load( const std::string& folderPath ) {
      std::vector<std::string> imagePaths = readFolder( folderPath );
      m_imageDatas = createImageDatas( imagePaths );
      const unsigned numThreads = std::thread::hardware_concurrency();
      std::vector<std::vector<std::weak_ptr<ImageData>>> splitDatas = 
        splitImageDatasToWeak( m_imageDatas, numThreads );
      for( auto& dataRangeToLoad : splitDatas )
        m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
   }
};

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

user2328447
источник
2

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.

MYLOGOS
источник
" разорвать круговые ссылки " как?
любопытный парень
2

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

Этот недостаток мы можем преодолеть с помощью weak_pointer.

Ашутош
источник
Как слабая ссылка может справиться с круговой зависимостью?
любопытный парень
1
@curiousguy, ребенок использует слабую ссылку на родителя, тогда родитель может быть освобожден, если на него нет общих (сильных) ссылок. Таким образом, при обращении к родителю через ребенка, слабая ссылка должна быть проверена, чтобы убедиться, что родитель все еще доступен. В качестве альтернативы, чтобы избежать этого дополнительного условия, механизм циклического отслеживания ссылок (либо разметка маркеров, либо проверка на уменьшение числа рефконтов, оба из которых имеют плохую асимптотическую производительность) может нарушать циклические общие ссылки, когда единственные общие ссылки на родительский и дочерний элементы получены от каждого из них. Другой.
Шелби Мур III
@ShelbyMooreIII " должен проверить, доступен ли родитель по-прежнему ", да, и вы должны быть в состоянии правильно реагировать на недоступный случай! Что не происходит с реальным (то есть сильным) реф. Это означает, что слабая ссылка не является заменой: она требует изменения в логике.
любопытный парень
2
@curiousguy Вы не спрашивали: «Как можно weak_ptrсправиться с циклической зависимостью без изменения логики программы в качестве замены для замены shared_ptr?» :-)
Шелби Мур III
2

Когда мы не хотим владеть объектом:

Пример:

class A
{
    shared_ptr<int> sPtr1;
    weak_ptr<int> wPtr1;
}

В приведенном выше классе wPtr1 не является владельцем ресурса, на который указывает wPtr1. Если ресурс удален, срок действия wPtr1 истекает.

Чтобы избежать круговой зависимости:

shard_ptr<A> <----| shared_ptr<B> <------
    ^             |          ^          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
class A           |     class B         |
    |             |          |          |
    |             ------------          |
    |                                   |
    -------------------------------------

Теперь, если мы сделаем shared_ptr классов B и A, use_count обоих указателей будет равен двум.

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

class B;

class A
{
    shared_ptr<B> sP1; // use weak_ptr instead to avoid CD

public:
    A() {  cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void setShared(shared_ptr<B>& p)
    {
        sP1 = p;
    }
};

class B
{
    shared_ptr<A> sP1;

public:
    B() {  cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }

    void setShared(shared_ptr<A>& p)
    {
        sP1 = p;
    }
};

int main()
{
    shared_ptr<A> aPtr(new A);
    shared_ptr<B> bPtr(new B);

    aPtr->setShared(bPtr);
    bPtr->setShared(aPtr);

    return 0;  
}

вывод:

A()
B()

Как видно из вывода, указатели A и B никогда не удаляются и, следовательно, происходит утечка памяти.

Чтобы избежать такой проблемы, просто используйте weak_ptr в классе A вместо shared_ptr, что имеет больше смысла.

Swapnil
источник
2

Я вижу std::weak_ptr<T>в качестве ручки для std::shared_ptr<T>: он позволяет мне получить, std::shared_ptr<T>если он все еще существует, но он не продлит срок его службы. Есть несколько сценариев, когда такая точка зрения полезна:

// Some sort of image; very expensive to create.
std::shared_ptr< Texture > texture;

// A Widget should be able to quickly get a handle to a Texture. On the
// other hand, I don't want to keep Textures around just because a widget
// may need it.

struct Widget {
    std::weak_ptr< Texture > texture_handle;
    void render() {
        if (auto texture = texture_handle.get(); texture) {
            // do stuff with texture. Warning: `texture`
            // is now extending the lifetime because it
            // is a std::shared_ptr< Texture >.
        } else {
            // gracefully degrade; there's no texture.
        }
    }
};

Другим важным сценарием является разрыв циклов в структурах данных.

// Asking for trouble because a node owns the next node, and the next node owns
// the previous node: memory leak; no destructors automatically called.
struct Node {
    std::shared_ptr< Node > next;
    std::shared_ptr< Node > prev;
};

// Asking for trouble because a parent owns its children and children own their
// parents: memory leak; no destructors automatically called.
struct Node {
    std::shared_ptr< Node > parent;
    std::shared_ptr< Node > left_child;
    std::shared_ptr< Node > right_child;
};

// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
    std::shared_ptr< Node > next;
    std::weak_ptr< Node > prev;
};

// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
    std::weak_ptr< Node > parent;
    std::shared_ptr< Node > left_child;
    std::shared_ptr< Node > right_child;
};

Херб Саттер отлично говорит о том, как лучше всего использовать языковые функции (в данном случае умные указатели), чтобы обеспечить утечку свободы по умолчанию (имеется в виду: все защелкивается на месте по конструкции; вряд ли это можно испортить). Это нужно смотреть.

Escualo
источник