Как стать автором
Обновить
31
0
Александр Дубовик @Dubovik_a

С++

Отправить сообщение
Почитайте внимательно мой пример BoozdedZomby.
И попробуйте его исправить, переделав на слабую ссылку.
А там и поговорим.
shared_from_this во всех примерах статьи является отличным инструментом для завязывания в узел.
Антипаттерн здесь вовсе не в shared_from_this, а использовании асинхронного чтения в runOnce

Эммм… Это как? Т.е. если мне поставлена задача написать асинхронную реализацию соединения — я должен ответить, что это — антипаттерн, и настаивать на том, чтобы отказаться от асинхронности в пользу блокирующих вызовов?
дожидаться завершения и уж потом выходить

Ага. Т.е. запущенный процесс ну никак нельзя прервать на уровне бизнес-логики. Просто прелесть.
А ничего, что в примерах из буста stream, в отличие от io_context, является полем данных класса зомби?
Лямбда — это всего лишь краткий способ попросить компилятор сгенерировать для меня класс с определёнными полями данных, определённым конструктором, определённой функцией operator(), а также создать его экземпляр.
Точно такого же эффекта я добьюсь, если напишу подобный класс руками. Только это более многословно.

А в примерах SimpleCyclic и PimplCyclic operator() всё равно не используется, т.е. достаточно только конструктора и поля данных.

Как собираетесь такие места выделять в коде для обращения на них повышенного внимания?
От std::shared_ptr там только название (более длинное, кстати), плюс повышенные накладные расходы по процессору и памяти.
А поведение — тадам — от сырого указателя.
Исходя из того, что внешние сущности успешно работают с возвращаемым кастратом std::shared_ptr — их устраивает именно поведение сырого указателя.
Вот его и надо возвращать, а не пытаться сделать «современно» — получается какое-то уродство.
Простите, а кто может «дёрнуть за _fn»? Он разве выходит наружу?
В коде нет никаких предпосылок думать, что _fn предназначен для какого-либо использования снаружи, тем более — после уничтожения родительского экземпляра SimpleCyclic (Вы же SimpleCyclic имеете в виду, говоря «в первом примере»?)
Более того, _fn в примере вообще никем и нигде не вызывается, даже внутри класса.
И написана эта конструкция именно так только по одной причине — это просто экстремально лаконичный способ организации циклической ссылки. И всё. Без высоких целей и идеологической подоплёки.
О чём статья — написано в заголовке: я предупредил в ней об опасности.

Надо ли разбирать методы устранения опасности — посмотрим по опросу через пару дней.

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

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

Предлагаю не обсуждать методы исправления — и так материал довольно объёмный, уже 60 комментов настрочили, а эта тема потянет ещё на целую статью такого же объёма.
Ну да — std::shared_ptr с пустым deleter. Который предоставляет пользователю, не знакомому с подробностями реализации, нечто с весьма неожиданными свойствами.
Зачем сначала брать умный указатель, а потом кастрировать его до состояния глупого, но только с повышенными накладными расходами? Если Вам не нужно поведение умного указателя — зачем Вы его используете?
Вот как раз тот код, который у Вас уже возник, и является плохим. Не надо так делать. А чтобы это понять — надо просто взять и попользоваться умными указателями в тех сценариях, для которых они предназначены. Хотя никто не заставляет — плюсы, к счастью, позволяют выбрать комфортное для себя подмножество языка и не выходить за его пределы (конечно, если этого не требует работодатель или ещё какие сильные обстоятельства).

Вообще-то, делал и это

И что, прям вот первой попыткой использования сырых указателей решили «а дай-ка я посмотрю, что будет»? Или может всё же пытались воспользоваться ими правильно, а ошибки иногда получались сами собой?
но всё обломала реализация языка

Это признак того, что Вы сражаетесь со своими инструментами.

Просто не нравится

«Просто не нравиться» может цвет обоев. Или вкус еды. Или музыка. Или текстура ткани.
А в технических решениях принято взвешивать «за» и «против», и выбирать в соответствии с этим. Если решения при взвешивании оказываются эквивалентными — ну что ж, тогда дальше выбор за автором. Иногда конечно говорят «мне это решение не нравится», но обычно это означает что-то типа «чутьё подсказывает, что это приведёт к плохому коду, но вербализовать недостатки вот прямо на ходу не могу», или «я вижу, что недостатки перевешивают и могу это вербализовать, но это долго, а Вы вроде как и сами должны их видеть». Это совсем не то, что «настоящее» «не нравится».

Ходить вдоль края и заходить на тёмную сторону конечно обязательно надо. Но зачем начинать-то с этого? Вы же не начинали изучение сырых указателей с попыток разыменования указателей на удалённые объекты?

Касательно кода из примера — в личку. Слишком далеко ушли от темы статьи.
Разве я не могу в тестовой функции создать все нужные классы на стеке, создать к ним shared_ptr без deleter'а и их уже передать в методы нужных классов?

Можете. Но зачем бороться с RAII вместо того, чтобы его использовать? Ну я не знаю — это как вытащить из микроскопа оптику, чтобы он не сломался при забивании гвоздей.

Умные указатели совместимы с сырыми только в одну сторону. Если Вам одна и та же штуковина в одном месте нужна в умном указателе, а в другом — в сыром, то проще создать в умном и отдать туда и туда.

не хочется всё создавать динамически

Почему?

Если Вы так не любите/не умеете пользоваться умными указателями — зачем тогда:
void CNet::SetInput(std::shared_ptr<CInput> cInput_Ptr)

?
Надо как-то согласовать сырой указатель с умным.
Сырой указатель берётся из операции взятия адреса у какой-то штуковины, о времени жизни которой кто-то уже заботится.
Умный указатель по умолчанию тоже заботится о времени жизни той штуковины, которой его проинициализировали.
Итого: поведение по умолчанию приведёт к двойному удалению, что является UB.
Вы хотите это поведение изменить.
Для этого надо, чтобы кто-то один перестал заботиться о времени жизни этой штуковины.

В случае с std::shared_ptr этого можно добиться подсовыванием пустого deleter. Аналога std::unique_ptr::release() в std::shared_ptr нет и быть не может, а если бы и была — она должна была бы вызываться постфактум, т.е. примерно в деструкторе CNet. А вот пустой deleter можно подсунуть в том месте, где будете наводить связь.
Ну а как перестать заботиться о времени жизни штуковины, не живущей в умном указателе? Вероятно, просто воспользоваться new без последующего delete, т.к. со штуковиной, созданной на стеке, так не получится.

Первый вариант приводит к странной спорной конструкции, нуждающейся в пояснениях комментарием.
Второй вариант естественным образом приводит примерно к:
auto input = std::shared_ptr<CInput>(new CInput);

, и далее — прямо к улучшенной версии:
auto input = std::make_shared<CInput>();


Первый вариант — редкостное извращение, второй вариант — обычное штатное использование std::shared_ptr.

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

Если по существу вопрса — лучше в личку.
При аккуратном использовании в силу понимания происходящего

Вот это вот ключевое.
При аккуратном использовании всё что угодно вполне себе торт — хоть топор, хоть змеиный яд, хоть атомная бомба. Но такая оговорка не делает приведённые вещи безопасными.
Если бы автору вопроса были «даны свыше» (из какой-нибудь библиотеки) и класс CData, и класс CNet в том виде, в котором они присутствуют в вопросе — то да, это была бы интересная задачка. Решение с пустым deleter — первое, что пришло в голову, и наверняка есть ещё 2-3 варианта получше.
Но, судя по всему, тут ещё не до полицейских разворотов — на второй передаче научиться бы ездить.
Литий не просто загорается. Он ещё и взрываться может.
Для аварии в такой системе достаточно замыкания всего лишь одного элемента.
Автору могу посоветовать найти толкового электронщика, чтобы помог обезопасить эту штуку.
Кратко перескажу содержание предыдущих серий (видимо, читать что статью, что комментарии Вам лень): если у Вас возникло желание использовать std::enable_shared_from_this — Вы в шаге от создания циклической ссылки. И при некоторых обстоятельствах это приведёт к трудонодетектируемой проблеме.
Промазал с комментарием.
Если хотите сложностей — смотрите в сторону std::shared_ptr с deleter-пустышкой. Но судя по вопросам, до корректного применения подобных техник Вам ещё года 2-3 покоддить бы… Ну и — я честно не понимаю, зачем плыть против течения.

Сырые указатели легко превращаются в сырые указатели на уничтоженные объекты. Если у Вас есть достаточные способы контроля жизни объектов — то пожалуйста.
Простой путь — это завести в классе CData поле именно типа std::shared_ptr (я надеюсь, что между классами CInputClass и CInput есть связь типа «наследование»?)
Если выставляете поля данных в public — это уже скорее struct, чем class.

Информация

В рейтинге
Не участвует
Откуда
Минск, Минская обл., Беларусь
Зарегистрирован
Активность