Comments 44
Почему-то не привели аналог boost:scoped_ptr<> из С++11 — std::unique_ptr<>.
+6
В С++11 (как и библиотеке Boost) существует ф-ция make_shared, используя которую совместно с ключевым словом auto, проблему boiler-plate можно решить так:
auto ptr = std::make_shared<MyNamespace::Object>(param1, param2, param3);
+8
Открою секрет, использование это функции по двум причинам лучше, чем вызов конструктора.
1) Не нужно 2 раза повторять имя класса ::std::shared_ptr(new T(...)) — короче
2) 1 аллокация памяти вместо двух. В случае ::std::shared_ptr(new T()), сначала вы вызываете аллокацию (new T), а затем shared_ptr для счетчика ссылок. В случае make_shared, вызывается одна аллокацию большого куска, в которую потом размещающим конструктором будет положен ваш объект. Так что этот способ еще и эффективнее.
1) Не нужно 2 раза повторять имя класса ::std::shared_ptr(new T(...)) — короче
2) 1 аллокация памяти вместо двух. В случае ::std::shared_ptr(new T()), сначала вы вызываете аллокацию (new T), а затем shared_ptr для счетчика ссылок. В случае make_shared, вызывается одна аллокацию большого куска, в которую потом размещающим конструктором будет положен ваш объект. Так что этот способ еще и эффективнее.
+2
а насколько это удобнее чем ручное выделение и освобождение?
-11
UFO just landed and posted this here
Может быть я какойто мутант с гипертрофированой кратковременной памятью, но у меня никогда не возникало проблем с тем чтоб прибрать за собой, а вот автоматические гарбачколлекторы всегда мне создавали неудобства.
-1
Обычно после подобной пары фраз от разных людей начинается Holy War.
0
Представьте себе что вы вызываете функцию и каждый раз должны думать не кидает ли она откуда-то внутри исключение? Если кидает — значит его нужно поймать, и внутри вызывать освобождение ресурсов. Но не всегда можно определить будет ли выброшено исключение или нет. К примеру у вас библиотека, вызывающая пользовательские коллбэки ( например виртуальные фукнции ) и вы в своем коде вообще не имеете представления что реально будет вызвано. Да вы можете каждый раз в таких случаях делать
но чем больше лишнего кода — тем выше вероятность ошибиться.
Умные же указатели просто снимают с вас заботу об огромном пласте языка. Причем совсем не обязательно чтобы они следили только за памятью — они могут беречь любой ресурс — будь то файл, сокет или еще какой-то хэндл.
Я уже молчу про то что они могут помочь при многопоточности, и еще раз гарантировать вам что объекты проживут ровно столько сколько они нужны.
catch(...)
{
freeMyMemory();
throw;
}
но чем больше лишнего кода — тем выше вероятность ошибиться.
Умные же указатели просто снимают с вас заботу об огромном пласте языка. Причем совсем не обязательно чтобы они следили только за памятью — они могут беречь любой ресурс — будь то файл, сокет или еще какой-то хэндл.
Я уже молчу про то что они могут помочь при многопоточности, и еще раз гарантировать вам что объекты проживут ровно столько сколько они нужны.
+4
Со временем на столько привыкаешь, что без них уже как-то не в кайф :)
+1
в два с половиной раза
+1
Еще забыли упоминуть, не только о том что могут быть выброшены исключения во время работы с выделенными ресурсами, и тогда такой код:
int *myPixels = new int[640 * 480];
// работаем
delete [] pixels;
Так же может генерировать утечки памяти, а вы написали — В принципе, никакой разницы.
int *myPixels = new int[640 * 480];
// работаем
delete [] pixels;
Так же может генерировать утечки памяти, а вы написали — В принципе, никакой разницы.
+1
Было бы хорошо добавить по unique_ptr и про weak_ptr.
+5
Мне кажется, что в вводной части статьи также необходимо хотябы дать ссылку на правило трех, а так — спасибо!
+1
Статья неполна без объяснений почему лучше использовать make_shared и без приёмов работы shared_ptr.
+4
make_shared актуально только в рамках C++11. В статье рассматривается C++03, я так понимаю.
А вот про что, имхо, следовало написать дополнительно, так это про то, что копирование shared_ptr может быть дорогим (QSharedPointer, std::shared_ptr) по этому не следует передавать его в функции по значению, за исключением случаев, когда это действительно надо.
А вот про что, имхо, следовало написать дополнительно, так это про то, что копирование shared_ptr может быть дорогим (QSharedPointer, std::shared_ptr) по этому не следует передавать его в функции по значению, за исключением случаев, когда это действительно надо.
0
boost::make_shared актуален и для c++
+2
Афаик, boost::make_shared не делает хитрого финта ушами при использование make_shared — shared_ptr занимает одинаково памяти, как при использование make_shared, так и при использование конструктора.
По крайней мере на Going Native STL говорил, что уменьшение размера shared_ptr это MS-овское ноу-хау, которому еще предстоит переползти в реализации других вендорав shared_ptr.
По крайней мере на Going Native STL говорил, что уменьшение размера shared_ptr это MS-овское ноу-хау, которому еще предстоит переползти в реализации других вендорав shared_ptr.
-1
Не из-за этого нужно использовать make_shared, не могу найти обсуждение этого, но вот 2 варианта кода:
и
Первый вариант может привести к утечке памяти, второй правильный.
void func(shared_ptr<A> a1, shared_ptr<A> a2);
func(shared_ptr<A>(new A()), shared_ptr<A>(new A()));
и
void func(shared_ptr<A> a1, shared_ptr<A> a2);
func(make_shared<A>(), make_shared<A>());
Первый вариант может привести к утечке памяти, второй правильный.
0
Да, вы чертовски правы. Применительно к auto_ptr об этом писал Саттер.
Касательно ноу-хау STL-я (человека) — ну так думаю этот патч в буст добавят очень быстро. Ибо boost::shared_ptr в ближайшие пару лет будет распространен значииииительно сильнее, чем std::shared_ptr в виду того, что первый поддерживается почти всеми компиляторами и кода с ним уже навалом, а последний только самыми свежими с поддержкой C++11 (или хотя бы TR1).
Касательно ноу-хау STL-я (человека) — ну так думаю этот патч в буст добавят очень быстро. Ибо boost::shared_ptr в ближайшие пару лет будет распространен значииииительно сильнее, чем std::shared_ptr в виду того, что первый поддерживается почти всеми компиляторами и кода с ним уже навалом, а последний только самыми свежими с поддержкой C++11 (или хотя бы TR1).
+1
почему копирование дорогое? потому что надо счётчик при вызове addref защитить от других потоков?
0
Ох, а зачем такие расстояния между строками в листингах? Тяжко читается.
+10
>При первом вызове delete все отлично, при втором вы потрете не те данные.
Мне кажется там не стирается ничего.
Мне кажется там не стирается ничего.
-1
Безопасно повторно удалять нулевой указатель, а ненулевой, указывающий на удаленный объект, — UB
+2
Дабл делет это уб. Там может стираться, например, все содержимое Вашего жесткого диска.
+1
Undefined Behavour не значит что комп может вдруг начать душить пользователя шнуром от мышки или вдруг стереть данные с диска. Иначе все эти «Программа совершила недопустимую операцию и будет закрыта» уже давно не оставили бы и следа от ваших данных.
Происходит на самом деле следующее. Указатель хранит адрес памяти. При вызове делит он должен его освободить, отдать обратно в систему. В случае если предыдущий вызов уже освободил этот адрес, то он уже отдан системе, а значит при повторной попытке освободить ресурс не принадлежащий вашей программе и происходит эта сама ошибка «данные не могут быть READ/WRITE» или что там еще.
Но бывает и иначе. К примеру память была освобождена, однако впоследствии вашей программе потребовалось еще немного памяти, система снова вам дала ее, и так совпало что та память которую вам вернула система — была та сама недавно удаленная. Событие не столь редкое как может показаться.
И тогда при двойном удалении — у нас возникает совершенно фееричная ситуация когда программа пытается удалить удаленную память, которая на самом деле заново выделенная и принадлежит совсем другому участку этой же программы. Вот тут начинается полный undefined bahavour.
К слову тоже самое может произойти не только с памятью но и с другим типом ресурсов — ведь почти все ресурсы внутри обозначаются как обычное целое число.
Представьте что вы работали с сокетом под номером 0xdf82, затем закрыли соединение, потом к вам пришло новое соединение, и вы начали работать с новым сокетом. Но то то что он новый не отменяет того факта что его идентификатор вполне может совпадать со старым, уже давно освобожденным значением.
Если же в какой-то переменной сохранилось старое значение, и где-то дальше по коду мы попробуем еще раз закрыть уже закрытое соединение ( чтобы наверняка, а то мало ли, кто эти компы знает ) то внезапно мы получим закрытие нашего нового соединения.
Происходит на самом деле следующее. Указатель хранит адрес памяти. При вызове делит он должен его освободить, отдать обратно в систему. В случае если предыдущий вызов уже освободил этот адрес, то он уже отдан системе, а значит при повторной попытке освободить ресурс не принадлежащий вашей программе и происходит эта сама ошибка «данные не могут быть READ/WRITE» или что там еще.
Но бывает и иначе. К примеру память была освобождена, однако впоследствии вашей программе потребовалось еще немного памяти, система снова вам дала ее, и так совпало что та память которую вам вернула система — была та сама недавно удаленная. Событие не столь редкое как может показаться.
И тогда при двойном удалении — у нас возникает совершенно фееричная ситуация когда программа пытается удалить удаленную память, которая на самом деле заново выделенная и принадлежит совсем другому участку этой же программы. Вот тут начинается полный undefined bahavour.
К слову тоже самое может произойти не только с памятью но и с другим типом ресурсов — ведь почти все ресурсы внутри обозначаются как обычное целое число.
Представьте что вы работали с сокетом под номером 0xdf82, затем закрыли соединение, потом к вам пришло новое соединение, и вы начали работать с новым сокетом. Но то то что он новый не отменяет того факта что его идентификатор вполне может совпадать со старым, уже давно освобожденным значением.
Если же в какой-то переменной сохранилось старое значение, и где-то дальше по коду мы попробуем еще раз закрыть уже закрытое соединение ( чтобы наверняка, а то мало ли, кто эти компы знает ) то внезапно мы получим закрытие нашего нового соединения.
+2
В случае с Вашей уютненькой вендой и Вашим уютненьким МС компилятором Вы корректно описали ситуацию, к которой приводит дабл делет. Но это не значит, что булгеон С++ компайлер из булгеон компайлер коллектион под БулгеонОС не отформатирует Ваш жесткий диск по дабл делет.
Конечно, дабл делет не приведет к форматированию жесткого диска в случае, когда вы пользуетесь распространенным C++ компилятором под распространенной ОС.
Конечно, дабл делет не приведет к форматированию жесткого диска в случае, когда вы пользуетесь распространенным C++ компилятором под распространенной ОС.
+5
> Undefined Behavour не значит что комп может вдруг начать душить пользователя шнуром от мышки или вдруг стереть данные с диска.
Глупости. Все знают, что при UB ваша программа отсылает боссу письмо с ругательствами.
Глупости. Все знают, что при UB ваша программа отсылает боссу письмо с ругательствами.
+1
Если этот указатель (по которому делаем delete) нулевой, то да.
Если не нулевой — память будет помечена свободной. И кто знает, что туда будет записано при следующем такте процессора.
Если не нулевой — память будет помечена свободной. И кто знает, что туда будет записано при следующем такте процессора.
+1
Под стиранием я имел в виду целенаправленное обнуление памяти или запись некого другого значения и собственно хотел это проянить. Судя потому что ответили ниже такого обычно не происходит.
0
В картинках: http://boostnotes.blogspot.com/.
+2
Статья понравилась, легко читается. Никогда не писал на С++, но всё было понятно :)
Правильно ли я понимаю, что это мини garbage collector для С++?
Правильно ли я понимаю, что это мини garbage collector для С++?
0
Это управляемый garbage collector. В том смысле что вы явно указываете какая память и как будет освобождаться, и при неправильном использовании все равно можете получить ошибки связанные с памятью, например если создать на основании одной памяти два независимых умных указателя — они удаляют ее по очереди так как не будут знать друг про друга.
0
больше похоже на automatic reference counting
0
Стоило бы отметить, что С++ позволяет создавать объекты в стеке, и тогда деструктор тоже вызывается автоматически, при выходе из scope, в котором создан объект.
0
А будет ли продолжение?
Интересует применение shared_ptr для массивов и наличие чего-то подобного для пары malloc/free.
Интересует применение shared_ptr для массивов и наличие чего-то подобного для пары malloc/free.
0
Обновите, пожалуйста, пост, разметка кода пострадала (
+1
Sign up to leave a comment.
Smart pointers для начинающих