C++ lectures at MIPT (in Russian). Lecture 9. Smart pointers, part 2

preview_player
Показать описание
Лекции в магистратуре МФТИ по C++ на русском языке.

Эта лекция посвящена несколько более глубоким вопросам умных указателей: разделяемым указателям, проблеме закольцовки и слабым указателям

Рассматриваются также интрузивные указатели и почему им не место в C++

Лектор: Константин Владимиров
Дата лекции: 3 декабря 2019 года
Съёмка и звук: Дмитрий Рябцев

Errata:

* пока здесь пусто
Рекомендации по теме
Комментарии
Автор

1:04:30 А почему нельзя хранить в памяти сначала ref counter и weak ref counter, и только потом данные, чтобы указатель указывал на ячейку с счётчиком, и чтобы можно было по удалению данных сказать компу "мне память после счётчиков больше не нужна, можешь её забрать"?

lzybqkf
Автор

1:06:17, слайд 75. Кажется ситуация с shared_ptr по безопасности относительно исключений не отличается от ситуации с unique_ptr с предыдущего слайда.
Потому что, хотя shared_ptr и аллоцирует память для контрольного блока, что может выбросить исключение, но shared_ptr дает гарантию, что если в конструкторе shared_ptr(Y*) будет выброшено исключение, то переданный указатель все-равно будет удален.
[util.smartptr.shared.const] 6: If an exception is thrown, delete p is called when T is not an array type, delete[] p otherwise.
И пункт 11 про то же самое, но для конструкторов shared_ptr с кастомным deleter'ом.

ivankorotkov
Автор

28:45
Смотрел я ваши лекции когда-то и после этого долго думал, что shared pointer'ы нельзя ковариантно преобразовывать без std::static_pointer_cast. Но ещё с 2011-го года у них есть конструктор от другого shared_ptr с convertible(11)/compatible типом. И сделать обычный static_cast тоже можно. И ошибок компиляции не будет ;)

elkadaf
Автор

1:05:19, слайд 74. Прошу поправить если не прав. Правильно ли понимаю, что в C++14 возможна ситуация, когда код
foo(unique_ptr<T>(new T(1)), unique_ptr<T>(new T(2)));
вызовет проблемы если компилятор выберет порядок выполнения, при котором сначала выполняются оба new и только затем они передаются в unique_ptr. Тогда если второй конструктор или оператор new выкинет исключение, то первый объект освобожден не будет, ведь его unique_ptr еще не успел сконструироваться?
И верно ли что в C++17 эта ситуация поправлена и теперь в данном сценарии разница между make_unique<T>() и std::unique_ptr<T>(new T()) сугубо стилистическая.

ivankorotkov
Автор

Умные указателя полезная вещь когда самому лень освобождать память, но только не в библиотеки QT(потомков QObject) когда всё завязано на обычных указателях.

kzhcgrx
Автор

1:13:46 Если будет shared_ptr<const string>, то как в эту const string делать write, тот который в COW? Только если ресетить одну из копий shared_ptr на другую строку. А если добавить const к самому shared_ptr, то оба его указателя: на контролблок и на данные станут константными и этот reset мы как раз сломаем, но если захотим поменять строку, то проблем быть вроде бы не должно: сам указатель на строку стал константным, а данные, на которые он указывает - нет. Будет просто write, без copy или я ошибаюсь?

samolisov
Автор

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

А если у нас контрольный блок вместе с данными, то Data pointer по идее вообще не нужен, т.к. совпадает с указателем на контрольный блок (либо имеет смещение, известное на этапе компиляции).

vasilystarostin
Автор

Ещё хотел спросить про применимость умных указателей вообще. В том же LLVM unique_ptr<Module> используется очень часто как тип результата различных порождающих функций. Во времена до C++17, когда copy elision не был обязательным, это было разумно, т.к. избавляло от возможного копирования большого модуля после порождения. LLVM на C++17 ещё не перешёл, но если абстрагироваться от проекта, то во времена C++17 можно возвращать из функций просто значение, главное, чтобы оно помещалось на стек. Получается через умный указатель есть смысл возвращать только какие-то большие объекты, которые на стек не помещаются или когда их размер априори неизвестен на этапе компиляции, как у дерева или связного списка.

samolisov
Автор

А нельзя ли решить проблему слабых интрузивных указателей с помощью shared_ptr таким образом:
Помимо reference counter мы заводим переменную shared_ptr<bool> aliveByIntrusivePtr_. Класс intrusive_weak_ptr хранил бы указатель на объект а также shared_ptr<bool> aliveByIntrusivePtr_. Таким образом мы как бы “шеруем” bool между intrusive_ptr и intrusive_weak_ptr. Как толко объект бы уничтожался, *aliveByIntrusivePtr_ получал бы значение false и мы таким образом знаем “живой” ли ещё объект на который указывает intrusive_weak_ptr. Протестировал. Кажется работает все.

timurtsotniashvili
Автор

В приведении (слайд 57) не раскрыта тема reintepret_pointer_cast

victormustya