- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
template<typename T>
class SharedPtr {
T* value;
int* ref_count;
public:
SharedPtr(T* value) : value(value) {
ref_count = new int;
*ref_count = 1;
}
SharedPtr(const SharedPtr& other) {
value = other.value;
ref_count = other.ref_count;
(*ref_count)++;
}
SharedPtr(SharedPtr&& other) {
value = other.value;
ref_count = other.ref_count;
other.ref_count = nullptr;
}
~SharedPtr() {
if (ref_count == nullptr) {
return;
}
if (*ref_count == 1) {
delete value;
delete ref_count;
} else {
(*ref_count)--;
}
}
T& operator *() const {
return *value;
}
T* operator ->() const {
return value;
}
};
Реалейзовал минимальную версию shared_ptr. Есть ошибки/замечания?
https://ideone.com/g7gqBM
Конструкторы не помечены, как explicit поэтому можно СЛУЧАЙНО отдать владение указателем функции принисающей SharedPtr
Главная проблема — это бесполезность данного класса. Его нельзя создать, чтобы заполнить потом. Нельзя заставить указывать на другой объект. Нельзя освободить указатель. Не помещается в контейнеры, использовать его как член класса из-за предыдущих проблем также затруднительно.
Итого: оба указателя унчтожены, а ref утёк.
Пишешь: "Этот класс не предназначен для одновременного доступа к объекту из нескольких потоков."
Если серьёзно, для начала count делаешь атомиком и переписываешь все доступы к нему на атомарные операции. В деструкторе сначала атомарно уменьшаешь count, затем смотришь, какое значение там реально было при декременте (проверяешь значение fetch_sub). На основании этого уже решаешь, удалять, или нет.
Защищать доступ к содержащемуся объекту — не твоя задача, об этом голова должна болеть у пользователя. Хотя можно сделать какой-нибудь 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], не знаю.
Вроде это сделали по аналогии с каким-то древним япом
В минимальной версии и без него достаточно. Оно ж просто не скомпилируется.
Ну как?
Какой багор ))) А не, норм, тот конст внутри T.
Константный указатель на тип Т.
~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? Сослаться на то, что клиент сам своё говно синхронизировать обязан?
Та зачем ref копьём проткнул? (((
Я бы лучше скобочки поставил, мало ли что.
А я боюсь так делать в крестах, у меня постоянно конпелятор ругается на подобное.
uwu
> А я боюсь так делать в крестах, у меня постоянно конпелятор ругается на подобное.
owo
Ну да, очень легко позвать что-то виртуальное и напороться на UB с pure virtual call.
Если конпелятор что-то скажет -- эт хуйня, чего тут бояться... Но он скорее промолчит и вызовет метод родителя вместо твоего. Особенно забавно, когда он НЕ абстрактный и прога сразу не падает.
Дурно пахнущая пессимизация. Ненужное сравнение, кторое сработает дай бог раз за 1000 использований.
Хотя в данном случае, если вдруг атомики оказались не lock-free, то возможно будет быстрее, чем стандартная альтернатива.
К тому же в том коде нет 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
>то мы еще должны заранее знать
да, ты должен всегда понимать кто на что ссылается. В некоторых средах на это есть гайдлайны.
Не хочется руками разгребать циклы -- кушайте ГЦ. Или есть еще более красивые варианты?
А если у меня просто граф из какой-то хуйни, который хуй пойми как перестраивается в процессе работы, и если я там буду придумывать, какая из хуитеней на что когда и как указывает как 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. Абстракция протекла, и даже тупой жабаобезъяне пришлось понимать , что куча -- не бездонная корова, чтоб туда срать