Нашли или выдавили из себя код, который нельзя назвать нормальным,
на который без улыбки не взглянешь?
Не торопитесь его удалять или рефакторить, — запостите его на
говнокод.ру, посмеёмся вместе!
Непотокобезопасно. Не поддерживает weak_ptr, нет возможности создать пустой указатель, освободить текущий указатель или присвоить ему другое значение. Если деструктор value бросает исключение — течёт.
Конструкторы не помечены, как explicit поэтому можно СЛУЧАЙНО отдать владение указателем функции принисающей SharedPtr
Починена только утечка при исключении. Всё остальное осталось. Даже краш при копировании перемещённого указателя.
Главная проблема — это бесполезность данного класса. Его нельзя создать, чтобы заполнить потом. Нельзя заставить указывать на другой объект. Нельзя освободить указатель. Не помещается в контейнеры, использовать его как член класса из-за предыдущих проблем также затруднительно.
Нет. Есть 2 указателя в 2х потоках. Один начинает уничтожаться, проверяет, что ref->count == 1, прыгает на else и отдаёт управление другому потоку. Второй поток уничтожает свой указатель, проверяет, что ref->count == 1, прыгает на else, уменьшает ref->count и возвращает управление потоку 1. Тот продолжает исполнять дальше: уменьшает ref->count и успокаивается.
Это всё не учитывая, что одновременная модификация count приводит к гонке и может испортить значение.
Пишешь: "Этот класс не предназначен для одновременного доступа к объекту из нескольких потоков."
Если серьёзно, для начала count делаешь атомиком и переписываешь все доступы к нему на атомарные операции. В деструкторе сначала атомарно уменьшаешь count, затем смотришь, какое значение там реально было при декременте (проверяешь значение fetch_sub). На основании этого уже решаешь, удалять, или нет.
Если ты хочешь, чтобы пирфоманс был подобен ПХП, то можно просто всё закрывать мютексом при любой операции. А так желательно, чтобы указатель был бы lock-free.
Защищать доступ к содержащемуся объекту — не твоя задача, об этом голова должна болеть у пользователя. Хотя можно сделать какой-нибудь shared_protected_resource, где ты будешь явно забирать ресурс.
Если ты хочешь, чтобы пирфоманс был подобен ПХП, то можно просто всё закрывать мютексом при любой операции. А так желательно, чтобы указатель был бы lock-free.
Защищать доступ к содержащемуся объекту — не твоя задача, об этом голова должна болеть у пользователя. Хотя можно сделать какой-нибудь shared_protected_resource, где ты будешь явно забирать ресурс.
Проблема в том, что все деструкторы — по-умолчанию noexcept, а вылетание исключения из noexcept деструктора приводит к мгновенной смерти программы (с очисткой всех ресурсов, ура (─‿‿─)!). Чтобы такого ня было — нужня добавить деструктору SharedPtr спецификатор "noexcept(noexcept(delete std::declval<T*>()))": https://wandbox.org/permlink/FRbyM2DP3XSWYcos .
В move-конструкторе не очищается чужое value. Это некритично (double delete не будет), но может приводить к трудноуловимым ошибкам.
Более серьёзно то, что в копирующем конструкторе, во-первых, не уменьшается текущий ref_count ("SharedPtr a = new ..., SharedPtr b = new ...; a = b;" — память под a утекла), а во-вторых — ня проверяется, что чужой ref_count != nullptr (падаем когда пытаемся копировать перемещённый SharedPtr).
А, operator=() нят. Тогда не течём, просто падаем.
Во всей стандартной библиотеке у перемещённых объектов можно вызывать методы без предусловий.
То есть я могу вызвать у вектора size() посмотреть, что там 0 и успокоится. Или вызвать push_back() и продолжить его использовать.
Как в стандарте написано: valid, but unspecified state.
lib.types.movedfrom:
1#
Objects of types defined in the C++ standard library may be moved from ([class.copy.ctor]). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
2#
An object of a type defined in the C++ standard library may be move-assigned ([class.copy.assign]) to itself. Unless otherwise specified, such an assignment places the object in a valid but unspecified state.
>valid but unspecified state.
То есть это не UB, но в какое именно состояние они впали мы не знаем?
Типа система в целом не сломалась, но они имеют правда быть в любом валидном состоянии?
Да, все инварианты ещё выполняются. То есть у вектора size, у которого нет предусловий, должен вернуть число, и если это число не 0, то 1й элемент существует и до него можно добраться. Если capacity > size, то push_back не переаллоцирует вектор и т.д.
В общем, после move мы должны считать объект свежеполученым и у нас нет никаких данных о его состоянии.
Лол, тогда у меня в коде есть сломанные классы (хотя это ведь требование тока для STL?).
В момент мува я опусташаю объект, и ставлю флажок, и дальше у него работает только десктрутор, а остальные методы вообще могут кинуть исключение, типа "инвалид стейт"
Эх
------
Вообще хуйня: если у тебя класс в момент создания создает некую сущность, которую он передает в момент мува
Что ему делать-то потом? новую сущность создавать?
Трудновыполнимое требование. Хорошо, что я не пишу STL
Если этот флажок можно проверить и в документации к классу прописано предусловие к этим методам, то почему бы и нет. Но конструкторы/операторы присваивания стоит сделать работающими в любых случаях.
Если этот флажок можно проверить и в документации к классу прописано предусловие к этим методам, то почему бы и нет. Но конструкторы/операторы присваивания стоит сделать работающими в любых случаях.
Ещё я не могу оставить ссылку на кишки нового объекта. Нужно либо аллоцировать под мувнутый объект массив, либо уметь работать с пустым указателем (когда 0 элементов в векторе).
Нужно отметить, что это касается стандартной библиотеки. Для своего кода ты можешь поставить предусловие "не был перемещён" для всех методов и радоваться жизни. Но следует обратить внимание на то, что может быть вызвано неявно. Обычно это деструктор, но если ты, скажем, перемещаешь объект из вектора, а потом он переаллоцируется, или из середины вектора удаляется значение, или его сортируют/применяют другой алгоритм то у твоего класса будет вызван копирующий/перемещающий конструктор/оператор присваивания. Ну и то, что использует конкретный алгоритм.
Нужно отметить, что это касается стандартной библиотеки. Для своего кода ты можешь поставить предусловие "не был перемещён" для всех методов и радоваться жизни. Но следует обратить внимание на то, что может быть вызвано неявно. Обычно это деструктор, но если ты, скажем, перемещаешь объект из вектора, а потом он переаллоцируется, или из середины вектора удаляется значение, или его сортируют/применяют другой алгоритм то у твоего класса будет вызван копирующий/перемещающий конструктор/оператор присваивания. Ну и то, что использует конкретный алгоритм.
Нужно отметить, что это касается стандартной библиотеки. Для своего кода ты можешь поставить предусловие "не был перемещён" для всех методов и радоваться жизни. Но следует обратить внимание на то, что может быть вызвано неявно. Обычно это деструктор, но если ты, скажем, перемещаешь объект из вектора, а потом он переаллоцируется, или из середины вектора удаляется значение, или его сортируют/применяют другой алгоритм то у твоего класса будет вызван копирующий/перемещающий конструктор/оператор присваивания. Ну и то, что использует конкретный алгоритм.
Я двинул объект из вектора, в векторе осталась его тушка безжизненная, и тут вектор решил по каким-то своим причинам его скопировать, и вызвал копирующий коснтруктор у класса, и передал туда эту тушку?
В новых крестах вектор вполне способен работать с някопируемыми объектами (ня будет работать часть операций, вроде копирования самого вектора и push_back(), но в целом всё будет хорошо). Ему нужня только перемещение.
Кажется, что может. Вектор должен быть последовательным. Если места последовательного нету, то он может вообще всё подвинуть куда-то. А потом обратно. нет?
А ты считай, что std::move и мув-конструкторы -- это такая оптимизация, которая может и не примениться. Как RVO/NRVO.
Жить станет намного легче. И сразу понятно будет, когда надо вилкой чистить самому, а когда и так сойдёт.
Ну т.е. ты вполне можешь кидать invalid state из методов мувнутого объекта. С тем же успехом ты их можешь кинуть (и скорее всего кидаешь?) и из сконструированного по-умолчанию. Т.к. он тоже в не особо юзабельном состоянии.
Это не тот "объект" что в крестах, у которого какой-то там овнершип, конструкторы, деструкторы, raii и прочая дрисня. И к ООП "объект" из Си никакого отношения не имеет.
> Овнершип в сишке тоже есть, на самом деле: если ты сделал malloc, то ты точно знаешь, когда нужно сделать free
Это не овнершип, это уже в голове программиста должно быть, что что-то где-то надо освободить. А можно и не освобождать, если это какая-то примитивная программа, которая допустим конвертит png в jpg используя какие-то либы, и потом завершается. ОСь сама приберет.
На эмбеддед системах я обычно статически память размечаю, и пофиг на malloc. Ну и можно всё прибирать по завершению треда, т.е. все поинтеры от malloc упихивать в какой-то стек, а потом его освободжать, если мы точно знаем, что эта malloc-нутая память используется только в контексте этого треда, и нигде больше. Вот тогда-то и появляется какое-то "владение", типа тред владеет этой памятью, и перед завершением он ее освободит всю.
Программа единственная, задач может быть много. И некоторые исполняются очень редко и не одновременно (обновление какое-нибудь, отправка данных и т.п.).
> Зачем вообще малок если у тебя на всей железке работает единственная программа?
Железка может уметь много чего делать, и ей может быть не нужно делать это одновременно (и памяти на одновременное делание всего у нее тоже нет). Поэтому железка может выделять память под хуйню№1, сделать хуйню№1, освободить память, выделить память под хуйню№2, сделать хуйню№2, освободить память. И эти хуйни№x может быть много, некоторые не сильно жрущие оперативку хуйни могут вполне работать одновременно, и они могут быть заранее неизвестны т.е. загружаться с внешнего флеш-накопителя или по блютузу какому-то скачиваться и устанавливаться, если это какая-то IoT-хуита. Так что для каких-то задач это вполне себе пригождается.
>Тащемто кресты же это как раз про то, чтоб автоматически делать то, что сишник делает вручную.
В крестах через эту вашу RAII-хуйню можно далеко не все, что можно руками. И RAII это не какая-то мегауниверсальная хуйня на все случаи, это просто еще один способ управлять ресурсами. См. https://govnokod.ru/27175#comment624086
Ну это вариант для реалтаймовых систем. В конце срока жизни вместо освобождения ресурса, добавляем его в список ресурсов на освобождение, а отдельный тред их уже прибирает, когда сможет. При желании, такое можно и на крестах намутить.
Можно поинтер на какую-то питушню кастануть в поинтер на структуру в которой массив из байтиков конечного размера. А потом еще другой какой-то поинтер тоже так кастануть. И можно присваивать
(но надо учесть всякую питушню с alingment и strict aliasing так что могут потребоваться доп. атрибуты)
Кстати, если я заверну массив в структуру, то получу нормальный хороший удобный тул практически с нулевым оверхедом, не?
почти как std:array в твоем любимом языке
Если конпелятор что-то скажет -- эт хуйня, чего тут бояться... Но он скорее промолчит и вызовет метод родителя вместо твоего. Особенно забавно, когда он НЕ абстрактный и прога сразу не падает.
Если конпелятор что-то скажет -- эт хуйня, чего тут бояться... Но он скорее промолчит и вызовет метод родителя вместо твоего. Особенно забавно, когда он НЕ абстрактный и прога сразу не падает.
Дурно пахнущая пессимизация. Ненужное сравнение, кторое сработает дай бог раз за 1000 использований.
Хотя в данном случае, если вдруг атомики оказались не lock-free, то возможно будет быстрее, чем стандартная альтернатива.
Обычно стоит писать так, чтобы было пофиг на то, что передаётся и без проверок.
К тому же в том коде нет strong exception safety. Утечек нет, но если что-то пойдёт не так при уничтожении старого значения при присваивании, то у тебя попортится старое значение, а присваивания не произойдёт. Ну и перемещающего присваивания нет.
Во-первых ни один подход — не серебрянная пуля, об этом следует помнить. Иногда решение, которое прекрасно работало сотни раз, оказывается субоптимальным.
Ну а во вторых, вот как может выглядеть оператор копирующего/перемещающего присваивания.
Обычно стоит писать так, чтобы было пофиг на то, что передаётся и без проверок.
К тому же в том коде нет strong exception safety. Утечек нет, но если что-то пойдёт не так при уничтожении старого значения при присваивании, то у тебя попортится старое значение, а присваивания не произойдёт. Ну и перемещающего присваивания нет.
Во-первых ни один подход — не серебрянная пуля, об этом следует помнить. Иногда решение, которое прекрасно работало сотни раз, оказывается субоптимальным.
Ну а во вторых, вот как может выглядеть оператор копирующего/перемещающего присваивания.
// у меня был какой-то объект в p
// и я его отдал другому объекту
foo->bar = std::move(p);
// теперь у меня в p есть неведомая хуйня, которую мне отдали взамен
// я её конечно затру или разрушу, но всё равно как-то неинтутивно
// особенно если там побочки в деструкторе
// у меня был объект в p и я его отдал
foo.bar = std::move(p);
// теперь у меня в p сконструированный по-умолчанию объект
// у меня был ещё один объект в q и я его тоже отдал
foo.bar = std::move(q);
// теперь у меня в q объект, который когда-то был в p
// не самое ожидаемое поведение
// у меня был объект в p и я его отдал
foo.bar = std::move(p);
// теперь у меня в p сконструированный по-умолчанию объект
// у меня был ещё один объект в q и я его тоже отдал
foo.bar = std::move(q);
// теперь у меня в q объект, который когда-то был в p
// не самое ожидаемое поведение, хоть и valid but unspecified
// у меня был какой-то объект
// и я его отдал другому объекту
foo->bar = std::move(p);
// теперь у меня есть неведомая хуйня, которую мне отдали взамен
// я её конечно затру или разрушу, но всё равно как-то неинтутивно
// особенно если там побочки в деструкторе
// у меня был какой-то объект в p
// и я его отдал другому объекту
foo->bar = std::move(p);
// теперь у меня в p есть неведомая хуйня, которую мне отдали взамен
// я её конечно затру или разрушу, но всё равно как-то неинтутивно
// особенно если там побочки в деструкторе
// у меня был объект в p и я его отдал
foo.bar = std::move(p);
// теперь у меня в p сконструированный по-умолчанию объект
// у меня был ещё один объект в q и я его тоже отдал
foo.bar = std::move(q);
// теперь у меня в q объект, который когда-то был в p
// не самое ожидаемое поведение, хоть и valid but unspecified
Кстати, я тут подумал. Чтобы гарантировать noexcept move assignment (которое иногда хочет видеть стандартная библиотека для некоторых оптимизаций) для классов с деструкторами, которые могут кидать исключения, как раз можно схитрить и воспользоваться noexcept swap чтобы отдать кишки левого объекта правому — раз мы ничего сейчас не уничтожаем, то перемещение будет noexcept. А исключение вылетит потом, когда будем уничтожать правый объект. Удачи в дебаге.
Хорошо что у меня всей этой хуйни в Си нет. Не нужно забивать голову какой-то внутриязыковой парашей, и можно сосредоточиться на самом программировании.
Меня не устраивают. Я не хочу себе мозг забивать говном, которое только в этом говноязыке имеет какой-то смысл. Потому что если ебать себе мозг таким ублюдским говном типа крестов, оно там прочно укореняется и ты уже начинаешь мыслить какими-то говнокатегориями этого языка, типа "так бля конструктор-деструктор хуё-моё, тут у нас rvalue копирующее noexcept кококо" тьфублядьнахуй... какие-то дегроды придумали какой-то дегродский говноязык с кучей ебанутых дегенератских правил и кучей исключений к каждому говноправилу. https://habr.com/ru/post/497114/
> Кто я такой? Я программист на плюсах. Я пишу код на плюсах две трети жизни. Это действительно стало огромной частью моей самоидентификации. Почему я претендую на синиора? Потому что я успел отстрелить себе миллион ног и натаскать свою нейросеть в черепушке определять, какая нога отстрелена на этот раз, по отсвету дульной вспышки. Потому что все интервью, касающиеся языка, я проходил, как тут говорят, with flying colors. Потому что я могу даже не готовиться к интервью, и всё равно его пройду. Потому что я могу показать особо интересные куски кода на гитхабе, и для меня скипнут как минимум телефонное интервью, а иногда скипали и техническое собеседование, разговаривая только о жизни или в лучшем случае о дизайне систем, релевантных для места работы.
> А так придётся выкидывать весь этот опыт в корзину и начинать почти с нуля. Ну, да, у меня есть какое-то там системное мышление, я дизайнил компиляторы, я дизайнил распределённые системы с противоречивыми требованиями, это, наверное, тоже кусок синьористости, но если меня разбудить ночью и спросить «почему ты претендуешь на синьора», я отвечу «потому что сиплюсплюс». Ко мне обращаются коллеги, когда там какой-то адовый баг или что-то не работает или надо что-то сделать на темплейтах или хорошее код-ревью бы. Это давно уже так — помню, ещё где-то на третьем курсе, когда я отрабатывал пропуски по физкультуре заплывами в бассейне, ко мне подплыл какой-то чувак и спросил: «а это ты 0xd34df00d? у меня тут вопрос по boost…»
Вообще, знания и навыки можно условно разделить на две категории.
Одни знания и навыки описывают некие фундаментальные вещи, ну например знания химии. У тебя не будет ситуации, когда твои знания по химии станут внезапно совсем не актуальны. Не, ну можно конечно представить что-то, например если тебя аниме-девочки заберут в альтернативную вселенную, где будут действовать другие законы физики, химии и прочее, но это уже на гране фантастики, понимаешь? В программировании такие знания/навыки это например умение писать всякие алгоритмы, ну допустим нормальный программист должен без проблем набросать по памяти какой-нибудь двусвязный список. В эту же категорию идет умение использовать циклы, рекурсию, оценивать временную сложность алгоритмов. Такая питушня не прибита гвоздями к говноязыку, она вполне кроссплатформенная в этом смысле
А есть знания, которые о какой-то созданной человеке хуитени, в которых ничего фундаментально-значимого нет. Типичный пример - инструкция использования какого-то сложного прибора с кучей кнопочек и переключателей. И если тебе в какой-то момент дадут другой прибор от другого производителя с другими кнопочками и переключателями, твои задротские навыки использования того прибора окажутся нахуй не нужны, понимаешь? Вот знания крестоговна - это как раз из этой области.
Вообще знания языка (даже такого сложного, как кресты) -- ничто, по сравнению со фундаментальщиной типа алгоритмов, структур данных, дискретки, архитектуры компьютеров, компиляторов и операционных систем, итд.
Ну вообще джава проще, конечно. Даже последняя. Толщина там скорее всего из-за того, что в одной книжке смешали не только язык, но и паттерны, многопоточность, какие-то практические применения и т.п.
Циклы, структуры данных, рекурсия, оценки сложности и т.п. -- это тоже не какие-то там фундаментальные законы физики, это знания о конкретной хуитени, придуманной человеком: цифровой вычислительной машине, которая пошагово выполняет твой код.
Да, это сейчас популярно. Но вангую что лет через 50-100 это всё уйдёт на помойку, как туда ушли знания об аналоговых ЭВМ.
З.Ы. Пригодится ли тебе умение писать циклы и рекурсию для вычислений на FPGA? Не думаю, мне первое время даже мешало.
Рекурсия есть и без ЭВМ. Например, рекурсивное деление клеток в организме человека (и не только человека).
Циклы тоже можно и без ЭВМ. Например, если тебе надо забить 200 гвоздей в ряд с расстоянием в 1 см между каждым, можно представить это себе как цикл, забить гвоздь по смещению 0 см влево относительно этой координаты, потом забить гвоздь по смещению 1 см влево относительно этой координаты, и так далее.
Циклы и рекурсия ближе к фундаментальной математике, чем к каким-то цифровым машинам. Они и не на цифровых машинах могут.
> З.Ы. Пригодится ли тебе умение писать циклы и рекурсию для вычислений на FPGA?
Ну если тебе надо экономить ячейки, и скорость не важна, вполне можно некую хрень саму на себя как-нибудь зациклить в схеме из логических элементов. Попробуй bigint умножение на FPGA реализовать для больших чисел.
Это факт, вон в факторио у программистов всегда первый вопрос "а как мне запилить if или for на кобенаторах?" Ну т.е. если ты всю жизнь мыслишь концепциями циклов и условий, то очень сложно принять что-то новое.
Есть и обратная проблема. Школьника на уроках алгебры обучили функциям, а потом на информатике показали рекурсивную процедуру на паскале, и он всё время пытается понять, чему "равно" proc(42).
Видел такое
Хотя императивщину, наверное, проще понять: всё так наш мир императивен
> Не факт, что какие-нибудь иноплане-тянки с альфы Центавра придут точно к таким же концепциям, даже если физика у них та же самая.
Та же химия/физика описывается во многих местах через рекурсию, вполне естественным образом. Всякие динамические системы там с диффурами есть. Допустим состояние системы в момент времени t2 зависит от состояния системы в момент времени t1 и у нас есть фукнция отображения state(t2) := do_transform(state(t1)) и тут кстати можно попробовать найти неподвижную точку, когда некий state(x-1) == do_transform(state(x-1)) т.е. когда состояние системы не меняется от времени.
Физика нашей реальности такой питушней хорошо описывается, и поэтому совершенно естественно, что такой формализм будет придуман для ее описания
Ты же не будешь утверждать, что такие инопланетянки могут построить космические корабли, но не будут знать, что 1+1=2, верно?
Предлагаю для начала придумать формальный метод, которым мы будем доказывать, что эти формализмы принципиально разные. А так же доказать, что наш формальный метод отличать формализм от формализма сам не является хуйней.
Функция — это наиболее эффективный алгоритм зожатия данных. Вместо хранения в памяти миллиона точек, составляющих окружность, можно хранить уравнение кривой, и вместо миллиона извилин тебе потребуется две с половиной.
Если законы физики у инопланетянок такие же, то и эволюционный отбор у них работает примерно так же, т.е. стремится на всём, включая размер мозга, сэкономить.
Допустим, у тебя есть кнопка и её слушатель.
Кнопка знает про слушателя, а слушатель -- про кнопку.
Если у них обоих будет друг на друга умный указатель, то они никогда не удалятся, бо будут держаться друг за друга как слипшиеся какашки.
Для решения этой проблемы в ЯП с RC есть weak ссылы/указатели. Они буквально означают "пусть тот, на кого у меня ссылка , умрет. Мне похуй. Я как-нить переживу".
У кнопки на слушателя shared_ptr (а больше ни у кого). Это значит, что когда кнопка умрет, должен будет умереть и слушатель.
У слушателя на кнопку weak_ptr. Перед обращением к нему слушатель проверяет её метод "expired". Если она expired, то ссылка невалидна.
> да, ты должен всегда понимать кто на что ссылается. В некоторых средах на это есть гайдлайны.
А если у меня просто граф из какой-то хуйни, который хуй пойми как перестраивается в процессе работы, и если я там буду придумывать, какая из хуитеней на что когда и как указывает как weak_ptr, а какая как обычный shared_ptr то это просто сраный пиздец получится. Придумывать еще какую-то ебанутую логику, что если такой-то shared_ptr переткнулся с той дрисни на ту дрисню в каком-то говнографе, то вот эта shared_ptr дрисня будет уже weak_ptr. Это будет просто адовый сраный пиздец, и работать это будет вероятно даже медленней, чем GC т.к. надо по этим говносвязям бегать и переставлять weak_ptr на shared_ptr и наоборот.
Так что в целом эта проблема, имхо, никак не решена. Заменить этой хуйней обычный GC тупо нельзя
Если в твоем графе тебе понадобится не только память чистить, но и закрывать ресурсы, то тебе всё равно придется строить граф на бумажечке, и понять, где папа, а где сын.
Если из всего множества программ выкинуть те, в которых:
* Граф очевиден
* Граф всё равно нужно понимать чтобы освобождать ресурсы
* Можно вообще ничего не освобождать, а сразу выделить память, и жить с ней до конца програмы
..то останутся случаи, для которых хорошо подходит GC. Их не так и много
У меня в сишке на контроллерах такой дрисни тупо нет, чтоб ее решать. У меня есть идея, как ее можно было бы решить через механизм, чем-то похожий на хуйню с shared_ptr/weak_ptr, но это слишком дохуя описывать. Может быть потом изложу мысль.
В языках с GC эта проблема тоже до конца не решена, на самом деле. GC решает её только для памяти, т.е. в нодах графа не может быть каких-то файловых дескрипторов и прочих ресурсов. Иначе один хер приходится перехуячивать граф в дерево или лес, как и для RC.
Если ты будешь делать вид, что память тебя не интересует, то она утечет. Именно потому в жабке тоже есть SoftReference и WeakReference. Абстракция протекла, и даже тупой жабаобезъяне пришлось понимать , что куча -- не бездонная корова, чтоб туда срать
У нас было 2 дескриптора, завёрнутых в акторы, 75 портов, линки, мониторы, io protocol, целое множество `with' всех сортов и расцветок. Не то чтобы это был необходимый запас для управления ресурсами, но если начал собирать дурь, становится трудно остановиться. Единственное, что вызывало у меня опасение — это lazy IO. Ничто в мире не бывает более беспомощным, безответственным и порочным, чем ленивое освобождение ресурсов. Я знал, что рано или поздно мы перейдем и на эту дрянь.
Конструкторы не помечены, как explicit поэтому можно СЛУЧАЙНО отдать владение указателем функции принисающей SharedPtr
Главная проблема — это бесполезность данного класса. Его нельзя создать, чтобы заполнить потом. Нельзя заставить указывать на другой объект. Нельзя освободить указатель. Не помещается в контейнеры, использовать его как член класса из-за предыдущих проблем также затруднительно.
Итого: оба указателя унчтожены, а ref утёк.
Пишешь: "Этот класс не предназначен для одновременного доступа к объекту из нескольких потоков."
Если серьёзно, для начала count делаешь атомиком и переписываешь все доступы к нему на атомарные операции. В деструкторе сначала атомарно уменьшаешь count, затем смотришь, какое значение там реально было при декременте (проверяешь значение fetch_sub). На основании этого уже решаешь, удалять, или нет.
Защищать доступ к содержащемуся объекту — не твоя задача, об этом голова должна болеть у пользователя. Хотя можно сделать какой-нибудь shared_protected_resource, где ты будешь явно забирать ресурс.
Защищать доступ к содержащемуся объекту — не твоя задача, об этом голова должна болеть у пользователя. Хотя можно сделать какой-нибудь shared_protected_resource, где ты будешь явно забирать ресурс.
Привет, Java Vector
привет, Java Hashtable
Ну и привет GILы, само собой
Ня самом деле нет, ня починена. Вернее, починена, но слишком радикальня:
https://wandbox.org/permlink/kPxhV0OcwNLbEvoA
> libc++abi: terminating with uncaught exception of type std::runtime_error: Hello!
Проблема в том, что все деструкторы — по-умолчанию noexcept, а вылетание исключения из noexcept деструктора приводит к мгновенной смерти программы (с очисткой всех ресурсов, ура (─‿‿─)!). Чтобы такого ня было — нужня добавить деструктору SharedPtr спецификатор "noexcept(noexcept(delete std::declval<T*>()))": https://wandbox.org/permlink/FRbyM2DP3XSWYcos .
Пошёл нахуй с моей ветки, гандон.
Бесплатная реализация X11 сервера.
Более серьёзно то, что в копирующем конструкторе, во-первых, не уменьшается текущий ref_count ("SharedPtr a = new ..., SharedPtr b = new ...; a = b;" — память под a утекла), а во-вторых — ня проверяется, что чужой ref_count != nullptr (падаем когда пытаемся копировать перемещённый SharedPtr).
А, operator=() нят. Тогда не течём, просто падаем.
А копирование перемещённого говна разве не UB должно быть?
То есть я могу вызвать у вектора size() посмотреть, что там 0 и успокоится. Или вызвать push_back() и продолжить его использовать.
Как в стандарте написано: valid, but unspecified state.
То есть у перемещенного вектора? Ну это пиздец какой-то. Надо руки отрывать за такое?
1#
Objects of types defined in the C++ standard library may be moved from ([class.copy.ctor]). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.
2#
An object of a type defined in the C++ standard library may be move-assigned ([class.copy.assign]) to itself. Unless otherwise specified, such an assignment places the object in a valid but unspecified state.
То есть это не UB, но в какое именно состояние они впали мы не знаем?
Типа система в целом не сломалась, но они имеют правда быть в любом валидном состоянии?
В общем, после move мы должны считать объект свежеполученым и у нас нет никаких данных о его состоянии.
В момент мува я опусташаю объект, и ставлю флажок, и дальше у него работает только десктрутор, а остальные методы вообще могут кинуть исключение, типа "инвалид стейт"
Эх
------
Вообще хуйня: если у тебя класс в момент создания создает некую сущность, которую он передает в момент мува
Что ему делать-то потом? новую сущность создавать?
Трудновыполнимое требование. Хорошо, что я не пишу STL
Нужно отметить, что это касается стандартной библиотеки. Для своего кода ты можешь поставить предусловие "не был перемещён" для всех методов и радоваться жизни. Но следует обратить внимание на то, что может быть вызвано неявно. Обычно это деструктор, но если ты, скажем, перемещаешь объект из вектора, а потом он переаллоцируется, или из середины вектора удаляется значение, или его сортируют/применяют другой алгоритм то у твоего класса будет вызван копирующий/перемещающий конструктор/оператор присваивания. Ну и то, что использует конкретный алгоритм.
Нужно отметить, что это касается стандартной библиотеки. Для своего кода ты можешь поставить предусловие "не был перемещён" для всех методов и радоваться жизни. Но следует обратить внимание на то, что может быть вызвано неявно. Обычно это деструктор, но если ты, скажем, перемещаешь объект из вектора, а потом он переаллоцируется, или из середины вектора удаляется значение, или его сортируют/применяют другой алгоритм то у твоего класса будет вызван копирующий/перемещающий конструктор/оператор присваивания. Ну и то, что использует конкретный алгоритм.
Нужно отметить, что это касается стандартной библиотеки. Для своего кода ты можешь поставить предусловие "не был перемещён" для всех методов и радоваться жизни. Но следует обратить внимание на то, что может быть вызвано неявно. Обычно это деструктор, но если ты, скажем, перемещаешь объект из вектора, а потом он переаллоцируется, или из середины вектора удаляется значение, или его сортируют/применяют другой алгоритм то у твоего класса будет вызван копирующий/перемещающий конструктор/оператор присваивания. Ну и то, что использует конкретный алгоритм.
Я двинул объект из вектора, в векторе осталась его тушка безжизненная, и тут вектор решил по каким-то своим причинам его скопировать, и вызвал копирующий коснтруктор у класса, и передал туда эту тушку?
Какой пиздец-то
Потому пихать в него сложные объекты лучше по ссылке/указателю ИМХО.
А напрямую сувать только тривиально копируемую мелочь типа структуры о двух полях
Жабаоёбы-скриптушки о такой хуйне не думают, лол.
Вернуть из функции массив объектов, каждый из которых представляет живое TCP соединение?
говно вопрос!
>мувабельные и копабельные объекты
А у листа какие требования?
Ладно, я научился читать:
T must meet the requirements of CopyAssignable and CopyConstructible.
У листа вообще только https://en.cppreference.com/w/cpp/named_req/Erasable, а остальные появляются по мере использования.
А ты считай, что std::move и мув-конструкторы -- это такая оптимизация, которая может и не примениться. Как RVO/NRVO.
Жить станет намного легче. И сразу понятно будет, когда надо вилкой чистить самому, а когда и так сойдёт.
Ну т.е. ты вполне можешь кидать invalid state из методов мувнутого объекта. С тем же успехом ты их можешь кинуть (и скорее всего кидаешь?) и из сконструированного по-умолчанию. Т.к. он тоже в не особо юзабельном состоянии.
У меня в Си нет той хуйни, из-за которой такая оптимизация могла бы понадобиться. Именно поэтому я за Си.
Вполне же есть, и означает он как раз кусочек памяти
В сишке у тебя есть объект int из 8 байт, и ты его туда-сюда копируешь.
А при копировании ООП объекта будут еще коснтрукторы/деструкторы вызываться
Овнершип в сишке тоже есть, на самом деле: если ты сделал malloc, то ты точно знаешь, когда нужно сделать free
зы:
object: region of data storage in the execution environment, the contents of which can represent values
(ANSI C)
Это не овнершип, это уже в голове программиста должно быть, что что-то где-то надо освободить. А можно и не освобождать, если это какая-то примитивная программа, которая допустим конвертит png в jpg используя какие-то либы, и потом завершается. ОСь сама приберет.
*Грустные звуки embedded систем*
Хотя в них и динамического выделения памяти обычно нет.
Программа единственная, задач может быть много. И некоторые исполняются очень редко и не одновременно (обновление какое-нибудь, отправка данных и т.п.).
Но это скорее оверлейки чем процессы.
Железка может уметь много чего делать, и ей может быть не нужно делать это одновременно (и памяти на одновременное делание всего у нее тоже нет). Поэтому железка может выделять память под хуйню№1, сделать хуйню№1, освободить память, выделить память под хуйню№2, сделать хуйню№2, освободить память. И эти хуйни№x может быть много, некоторые не сильно жрущие оперативку хуйни могут вполне работать одновременно, и они могут быть заранее неизвестны т.е. загружаться с внешнего флеш-накопителя или по блютузу какому-то скачиваться и устанавливаться, если это какая-то IoT-хуита. Так что для каких-то задач это вполне себе пригождается.
В крестах ты тоже можешь вручную это хендлить.
Тащемто кресты же это как раз про то, чтоб автоматически делать то, что сишник делает вручную.
В крестах через эту вашу RAII-хуйню можно далеко не все, что можно руками. И RAII это не какая-то мегауниверсальная хуйня на все случаи, это просто еще один способ управлять ресурсами. См. https://govnokod.ru/27175#comment624086
>Допустим можно освобождать ресурсы в отдельном треде для освобождения ресурсов, и делать это во время простоев
Не изобрети случайно ГЦ, пожалуйста
Кстати, почему в "си" нельзя скопировать массив присваиваением?
Нельзя же?
Из-за автокаста в поинтер.
Массивы существуют только как структура данных, а любое обращение к ним превращается в работу с поинтерами. Ну кроме sizeof.
(но надо учесть всякую питушню с alingment и strict aliasing так что могут потребоваться доп. атрибуты)
Но это будет UB? Это же не структуры с одинаковым началом и не поля юниона.
Думаю тут надо __attribute__ ((aligned (1), packed, may_alias)). Тогда UB не будет.
И эти люди нядовольны std::move()!
Почему было их не копировать как обычные структуры?
Если я хочу передать массив в функцию по указателю, ну и писал бы &petuh или &petuh[0], не знаю.
Вроде это сделали по аналогии с каким-то древним япом
Можно присваиванием скопировать структуру с массивом внутри. Массив тогда тоже скопируется
Кстати, если я заверну массив в структуру, то получу нормальный хороший удобный тул практически с нулевым оверхедом, не?
почти как std:array в твоем любимом языке
В минимальной версии и без него достаточно. Оно ж просто не скомпилируется.
Ну как?
Какой багор ))) А не, норм, тот конст внутри T.
Константный указатель на тип Т.
Константный указатель на тип Т.
~SharedPtr() noexcept(noexcept(destroy()))
void destroy() noexcept(noexcept(delete std::declval<T*>()))
SharedPtr(SharedPtr&& other) noexcept // move-конструкторы и move-операторы присваивания должны быть noexcept
~SharedPtr() noexcept(noexcept(destroy()))
void destroy() noexcept(noexcept(delete std::declval<T*>()))
SharedPtr(SharedPtr&& other) noexcept // move-конструкторы и move-операторы присваивания должны быть noexcept
В самом shared_ptr и его Ref никаких гонок, по идее, нет, т.е. хватило бы relaxed'а.
Но что делать с delete value? Сослаться на то, что клиент сам своё говно синхронизировать обязан?
В самом shared_ptr и его Ref никаких гонок, по идее, нет, т.е. хватило бы relaxed'а.
Но что делать с delete value? Сослаться на то, что клиент сам своё говно синхронизировать обязан?
Та зачем ref копьём проткнул? (((
Я бы лучше скобочки поставил, мало ли что.
А я боюсь так делать в крестах, у меня постоянно конпелятор ругается на подобное.
uwu
> А я боюсь так делать в крестах, у меня постоянно конпелятор ругается на подобное.
owo
uwu
> А я боюсь так делать в крестах, у меня постоянно конпелятор ругается на подобное.
owo
Ну да, очень легко позвать что-то виртуальное и напороться на UB с pure virtual call.
Если конпелятор что-то скажет -- эт хуйня, чего тут бояться... Но он скорее промолчит и вызовет метод родителя вместо твоего. Особенно забавно, когда он НЕ абстрактный и прога сразу не падает.
Если конпелятор что-то скажет -- эт хуйня, чего тут бояться... Но он скорее промолчит и вызовет метод родителя вместо твоего. Особенно забавно, когда он НЕ абстрактный и прога сразу не падает.
Дурно пахнущая пессимизация. Ненужное сравнение, кторое сработает дай бог раз за 1000 использований.
Хотя в данном случае, если вдруг атомики оказались не lock-free, то возможно будет быстрее, чем стандартная альтернатива.
К тому же в том коде нет strong exception safety. Утечек нет, но если что-то пойдёт не так при уничтожении старого значения при присваивании, то у тебя попортится старое значение, а присваивания не произойдёт. Ну и перемещающего присваивания нет.
Во-первых ни один подход — не серебрянная пуля, об этом следует помнить. Иногда решение, которое прекрасно работало сотни раз, оказывается субоптимальным.
Ну а во вторых, вот как может выглядеть оператор копирующего/перемещающего присваивания.
Реализацию swap оставляю на самостоятельное изучение.
К тому же в том коде нет strong exception safety. Утечек нет, но если что-то пойдёт не так при уничтожении старого значения при присваивании, то у тебя попортится старое значение, а присваивания не произойдёт. Ну и перемещающего присваивания нет.
Во-первых ни один подход — не серебрянная пуля, об этом следует помнить. Иногда решение, которое прекрасно работало сотни раз, оказывается субоптимальным.
Ну а во вторых, вот как может выглядеть оператор копирующего/перемещающего присваивания.
Реализацию swap оставляю на самостоятельное изучение.
Мой объект просто мувается в аргумент, а старое значение разрушается на выходе из оператора.
Когда-нибудь и я уйду в монахи... Но пока кресты вполне устраивают.
https://habr.com/ru/post/497114/
> Кто я такой? Я программист на плюсах. Я пишу код на плюсах две трети жизни. Это действительно стало огромной частью моей самоидентификации. Почему я претендую на синиора? Потому что я успел отстрелить себе миллион ног и натаскать свою нейросеть в черепушке определять, какая нога отстрелена на этот раз, по отсвету дульной вспышки. Потому что все интервью, касающиеся языка, я проходил, как тут говорят, with flying colors. Потому что я могу даже не готовиться к интервью, и всё равно его пройду. Потому что я могу показать особо интересные куски кода на гитхабе, и для меня скипнут как минимум телефонное интервью, а иногда скипали и техническое собеседование, разговаривая только о жизни или в лучшем случае о дизайне систем, релевантных для места работы.
(у автора конечно, не у тебя)
Одни знания и навыки описывают некие фундаментальные вещи, ну например знания химии. У тебя не будет ситуации, когда твои знания по химии станут внезапно совсем не актуальны. Не, ну можно конечно представить что-то, например если тебя аниме-девочки заберут в альтернативную вселенную, где будут действовать другие законы физики, химии и прочее, но это уже на гране фантастики, понимаешь? В программировании такие знания/навыки это например умение писать всякие алгоритмы, ну допустим нормальный программист должен без проблем набросать по памяти какой-нибудь двусвязный список. В эту же категорию идет умение использовать циклы, рекурсию, оценивать временную сложность алгоритмов. Такая питушня не прибита гвоздями к говноязыку, она вполне кроссплатформенная в этом смысле
А есть знания, которые о какой-то созданной человеке хуитени, в которых ничего фундаментально-значимого нет. Типичный пример - инструкция использования какого-то сложного прибора с кучей кнопочек и переключателей. И если тебе в какой-то момент дадут другой прибор от другого производителя с другими кнопочками и переключателями, твои задротские навыки использования того прибора окажутся нахуй не нужны, понимаешь? Вот знания крестоговна - это как раз из этой области.
Не понимаю, зачем о них волноваться
Ну если сопоставить размер книги по крестам от Страуструпа с размером книги Танненбаума по ОС... вполне сопоставимы.
Видимо забивать мозг этой крестохуйней нужно в сопоставимой степени, чтобы считаться сеньор-крестоговно-девелопером
Почему мы не слышим таких статей от джава программистов?
У джавы довольно развесистая стандартная либлиотека, особенно если включить в неё JavaEE. Может быть книжка большая потому.
Да, это сейчас популярно. Но вангую что лет через 50-100 это всё уйдёт на помойку, как туда ушли знания об аналоговых ЭВМ.
З.Ы. Пригодится ли тебе умение писать циклы и рекурсию для вычислений на FPGA? Не думаю, мне первое время даже мешало.
Циклы тоже можно и без ЭВМ. Например, если тебе надо забить 200 гвоздей в ряд с расстоянием в 1 см между каждым, можно представить это себе как цикл, забить гвоздь по смещению 0 см влево относительно этой координаты, потом забить гвоздь по смещению 1 см влево относительно этой координаты, и так далее.
Циклы и рекурсия ближе к фундаментальной математике, чем к каким-то цифровым машинам. Они и не на цифровых машинах могут.
Ну если тебе надо экономить ячейки, и скорость не важна, вполне можно некую хрень саму на себя как-нибудь зациклить в схеме из логических элементов. Попробуй bigint умножение на FPGA реализовать для больших чисел.
Но в коде это не выглядит как цикл! Умение писать циклы на традиционных языках тут вообще только мешает понять новую концепцию.
З.Ы. Это ближе к чему-то функциональному в духе map/reduce.
Или можно хаскель взять какой-нибудь, там циклов тоже нет, и что?
Это факт, вон в факторио у программистов всегда первый вопрос "а как мне запилить if или for на кобенаторах?" Ну т.е. если ты всю жизнь мыслишь концепциями циклов и условий, то очень сложно принять что-то новое.
Видел такое
Хотя императивщину, наверное, проще понять: всё так наш мир императивен
Так Эрланг появился. Джо Армстронгу (RIP) тоже подобный анскилл не понравился.
Скажи это электронам и прочим фотонам.
Выдуманная человеком хуйня, как и вся математика.
Не факт, что какие-нибудь иноплане-тянки с альфы Центавра придут точно к таким же концепциям, даже если физика у них та же самая.
Та же химия/физика описывается во многих местах через рекурсию, вполне естественным образом. Всякие динамические системы там с диффурами есть. Допустим состояние системы в момент времени t2 зависит от состояния системы в момент времени t1 и у нас есть фукнция отображения state(t2) := do_transform(state(t1)) и тут кстати можно попробовать найти неподвижную точку, когда некий state(x-1) == do_transform(state(x-1)) т.е. когда состояние системы не меняется от времени.
Физика нашей реальности такой питушней хорошо описывается, и поэтому совершенно естественно, что такой формализм будет придуман для ее описания
Ты же не будешь утверждать, что такие инопланетянки могут построить космические корабли, но не будут знать, что 1+1=2, верно?
Если законы физики у инопланетянок такие же, то и эволюционный отбор у них работает примерно так же, т.е. стремится на всём, включая размер мозга, сэкономить.
Пиздец (( Пока по твоей ссылке не понял что это и как это хуетой пользоваться.
Кнопка знает про слушателя, а слушатель -- про кнопку.
Если у них обоих будет друг на друга умный указатель, то они никогда не удалятся, бо будут держаться друг за друга как слипшиеся какашки.
Для решения этой проблемы в ЯП с RC есть weak ссылы/указатели. Они буквально означают "пусть тот, на кого у меня ссылка , умрет. Мне похуй. Я как-нить переживу".
У кнопки на слушателя shared_ptr (а больше ни у кого). Это значит, что когда кнопка умрет, должен будет умереть и слушатель.
У слушателя на кнопку weak_ptr. Перед обращением к нему слушатель проверяет её метод "expired". Если она expired, то ссылка невалидна.
Ключом к понимаю weak_ptr является вот этот метод:
https://en.cppreference.com/w/cpp/memory/weak_ptr/expired
Вот пример из Swift
https://www.programmersought.com/article/1272854504/
Хуёво она решена.
См. https://govnokod.ru/27340#comment621591
Хуёво она решена (если это вообще можно назвать решением).
См. https://govnokod.ru/27340#comment621591
>то мы еще должны заранее знать
да, ты должен всегда понимать кто на что ссылается. В некоторых средах на это есть гайдлайны.
Не хочется руками разгребать циклы -- кушайте ГЦ. Или есть еще более красивые варианты?
А если у меня просто граф из какой-то хуйни, который хуй пойми как перестраивается в процессе работы, и если я там буду придумывать, какая из хуитеней на что когда и как указывает как weak_ptr, а какая как обычный shared_ptr то это просто сраный пиздец получится. Придумывать еще какую-то ебанутую логику, что если такой-то shared_ptr переткнулся с той дрисни на ту дрисню в каком-то говнографе, то вот эта shared_ptr дрисня будет уже weak_ptr. Это будет просто адовый сраный пиздец, и работать это будет вероятно даже медленней, чем GC т.к. надо по этим говносвязям бегать и переставлять weak_ptr на shared_ptr и наоборот.
Так что в целом эта проблема, имхо, никак не решена. Заменить этой хуйней обычный GC тупо нельзя
Если в твоем графе тебе понадобится не только память чистить, но и закрывать ресурсы, то тебе всё равно придется строить граф на бумажечке, и понять, где папа, а где сын.
Если из всего множества программ выкинуть те, в которых:
* Граф очевиден
* Граф всё равно нужно понимать чтобы освобождать ресурсы
* Можно вообще ничего не освобождать, а сразу выделить память, и жить с ней до конца програмы
..то останутся случаи, для которых хорошо подходит GC. Их не так и много
Суть там в https://en.wikipedia.org/wiki/Hypergraph
Если ты будешь делать вид, что память тебя не интересует, то она утечет. Именно потому в жабке тоже есть SoftReference и WeakReference. Абстракция протекла, и даже тупой жабаобезъяне пришлось понимать , что куча -- не бездонная корова, чтоб туда срать