вторник, 21 февраля 2012 г.

Неглупые указатели (smart_pointers)

Студент увидел как-то на рынке, что какой-то мужик 
продает мозги разных специалистов. Заинтересовался, 
подходит, спрашивает что почем. Ему объясняют: 
«Это мозги математика, они по сто рублей за килограмм, 
это – мозги физика, они – по двести за кило, а вот эти
 – мозги философа, они самые дорогие, по две тысячи». 
Студент проникся уважением, спрашивает: 
«Что, философские мозги и правда такие хорошие?» 
«Да нет, дело не в этом, просто представь, это же 
сколько философов отловить надо, чтобы набрать 
целый килограмм мозгов!»


  Прочитав Скотта Майерса, я стала противником auto_ptr. Auto_ptr - это зло-зло! Я об этом сказала своему начальнику. Он же сделал круглые глаза и выразил удивление, чем меня сильно озадачил. Я опять повторила: "Зло-Зло!". А он: "Почему?". Когда я объяснила, почему зло, он сделал простейший вывод: "Просто не надо их использовать в контейнерах." Действительно, подумала я... :) 

  И так небольшой обзор умных указателей: 

 STL: 
  • auto_ptr - только не для использования в контейнерах!! (См. Скотт Майерс, "Effective STL", совет №8)
  • weak_ptr - (доступен только в ISO/IEC 14882:2011, или при указании опции компиляции -std=c++0x) 
  • shared_ptr - (доступен только в ISO/IEC 14882:2011, или при указании опции компиляции -std=c++0x) 

   И вообще, в файлы с новым стандартом лучше не заглядывать - незаметно сносит крышу. То есть вроде бы всё на месте, а холодком веет. Необычные конструкции вроде таких, пугают:  shared_ptr(shared_ptr&& __r), но об этом можно почитать здесь.

 Boost: 
  • scoped_ptr - самый простой умный указатель. Как написано в комментариях - simple solution for simple needs. Умеет возвращать память при выходе из области видимости (в деструкторе). Умеет делать get(), reset(), swap();
  • intrusive_ptr - умный указатель со встроенным счетчиком ссылок. Может быть создан из произвольного raw-указателя (сырого указателя) типа T*. Блок памяти для intrusive_ptr такой же, как и для соответствующего raw-указателя. Умеет делать get(), reset(), swap(). Но для него придется определить функции подсчета ссылок и освобождения памяти. В конструкторе intrusive_ptr( T * p, bool add_ref = true ) вторым параметром (как это ясно из названия) можно повлиять на увеличение счетчика в первый раз. Его удобно использовать при разработке, где требуется самому определять не только, как освободить память, но и как работает счетчик ссылок. Главное правильно, если не ясно какой из двух использовать: его или shared_ptr, попробуйте использовать сначала shared_ptr;
  • weak_ptr - слабенький указатель :). Используется совместно с  shared_ptr. Хранит ссылку на объект, которым уже владеет shared_ptr. Для доступа к хранимому объекту нужно перейти к shared_ptr, используя соответствующий  конструктор: template<class Y> explicit shared_ptr(weak_ptr<Y> const & r). Есть внутренние переменные: указатель на данные, счетчик ссылок (boost::detail::weak_count). Умеет делать lock() - защита от удаления объекта и сказать, что объект уже никем не используется с помощью функции expired();
  • shared_ptr - умный указатель посложнее. Есть внутренние переменные - указатель на данные, счетчик ссылок (boost::detail::shared_count). Его использование гарантирует, что объект будет удален, когда последний shared_ptr, указывающий на него, уничтожится или сбросится (reset()).