|
2 / 2 / 0
Регистрация: 16.08.2013
Сообщений: 86
|
||||||
Как избежать использования разрушенной переменной?16.11.2023, 02:12. Показов 2542. Ответов 27
Метки нет (Все метки)
Никак не могу найти приемлимое решение следующей проблемы.
Допустим, мы имеем логирующую функцию log(), которая может быть вызвана из любого места программы. Например:
Вижу следующие варианты, которые мне не совсем нравятся: * Не создавать в деструкторах зависимость от других глобальных переменных. В данном примере - не вызывать log() из ~A(). Но ведь можно представить ситуацию, что в деструкторе ожидается завершение параллельного потока, который как раз может использовать log()... * Переместить в самый верх кода подобные глобальные переменные, делая их последними в очереди на разрушение. В данном примере - переместить "m" наверх. Но тогда реализация функции log() будет разделена на 2 части...
0
|
||||||
| 16.11.2023, 02:12 | |
|
Ответы с готовыми решениями:
27
Как избежать использования оператора goto? Как избежать использования кучи проверок? Как избежать утечки памяти: нюансы использования QList |
|
7804 / 6568 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
|
|
| 16.11.2023, 10:21 | |
|
0
|
|
|
фрилансер
6454 / 5655 / 1129
Регистрация: 11.10.2019
Сообщений: 15,054
|
|||
| 16.11.2023, 10:32 | |||
|
Добавлено через 1 минуту
0
|
|||
|
19497 / 10102 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
|
|||
| 16.11.2023, 10:35 | |||
|
0
|
|||
|
7804 / 6568 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
|
|
| 16.11.2023, 10:37 | |
|
DrOffset, а конкретно в его случае static в функции когда уничтожается? Просто в общем порядке обявления с этими a, b? Это вообще как-то регламентировано?
0
|
|
|
19497 / 10102 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
|
|||
| 16.11.2023, 10:57 | |||
|
У него в коде сначала будет создание объекта a, затем посредством вызова функции создастся локальный статический объект m, потом объект b.Добавлено через 10 минут Не смотря на то, что ТС скорее всего это не понравится, единственное нормальное решение здесь - это избавляться от глобальных объектов совсем, если в их деструкторах предполагается такая сложная взаимологика.
1
|
|||
|
7804 / 6568 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
|
|
| 16.11.2023, 11:38 | |
|
DrOffset, с созданием понятно, а уничтожение? Не очевидно, что это зависит от положения функции в файле.
0
|
|
|
19497 / 10102 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
|
||||
| 16.11.2023, 12:06 | ||||
|
Порядок создания глобальных объектов зависит от порядка их объявления в файле. А локальный статический объект создается в момент первого обращения. Если вы написали такой код, который вклинивает первое обращение между созданием двух глобальных объектов, ну что ж, значит так тому и быть: уничтожение будет в порядке обратном созданию. Потому что у всех этих объектов один и тот же storage duration.
1
|
||||
|
2 / 2 / 0
Регистрация: 16.08.2013
Сообщений: 86
|
|||||||
| 16.11.2023, 15:23 [ТС] | |||||||
![]()
0
|
|||||||
|
фрилансер
6454 / 5655 / 1129
Регистрация: 11.10.2019
Сообщений: 15,054
|
|
| 16.11.2023, 16:49 | |
|
0
|
|
|
19497 / 10102 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
|
||
| 16.11.2023, 16:59 | ||
|
Его суть в том, что объект может быть уничтожен, но синглтон написан так, что "возрождает" его, если происходит обращение к "трупу" . Для вашего класса "логгер" может подойти, но если требуется сохранять состояние от одной жизни к другой, то могут возникнуть сложности. В любом случае это решение "не очень", потом что архитектурно оно кривое. Избавление от глобальных взаимосвязей гораздо более надежная вещь.
1
|
||
|
7804 / 6568 / 2988
Регистрация: 14.04.2014
Сообщений: 28,705
|
|
| 16.11.2023, 17:25 | |
|
Вроде бы нашёл в разделе Termination. Витиевато написано.
If the completion of the constructor or dynamic initialization of an object with static storage duration strongly happens before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first. If the completion of the constructor or dynamic initialization of an object with thread storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first. Добавлено через 3 минуты
0
|
|
|
2 / 2 / 0
Регистрация: 16.08.2013
Сообщений: 86
|
||||||
| 16.11.2023, 19:29 [ТС] | ||||||
|
Кажется, нашёл приемлемое решение.
Проблема описана здесь (не уверен, разрешены ли здесь ссылки на сторонние ресурсы): 1: How do I prevent the "static initialization order problem". 2: Why doesn’t the Construct On First Use Idiom use a static object instead of a static pointer?. Смысл следующий. Чтобы контролировать порядок создания глобальной переменной, нужно обернуть её в функцию, дописать static (как это сделано в функции log() в моём примере) и вызвать в нужном месте. А чтобы гарантировать неразрушенность этой переменной в каком-нибудь "глобальном" деструкторе, нужно сделать пробный "создающий" вызов в соответствующем конструкторе. Таким образом, мой пример станет выглядеть вот так (изменилась лишь строка #8):
Но я ничего такого при беглом гуглении не нашёл.
0
|
||||||
|
19497 / 10102 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
|
||
| 16.11.2023, 20:42 | ||
![]() Как я уже сказал, это решение не будет гарантированно работать, если глобальные объекты, использующие эту функцию, находятся в разных ЕТ. Т.е. в пределах одного файла ваше решение сработает, если файлов несколько - не факт. Да, там по вашей ссылке есть еще один вариант решения: паттерн Nifty Counter, но вы что-то про него не сказали ничего, и ваш пример решением проблемы в целом не является.
0
|
||
|
2 / 2 / 0
Регистрация: 16.08.2013
Сообщений: 86
|
||
| 17.11.2023, 05:11 [ТС] | ||
|
Можно как-то убедиться в этом утверждении? Потому как я не увидел где-то упоминаний (например, читая здесь и здесь), что данное решение может иметь проблемы при использовании разных единиц трансляции. Плюс, моя собственная проверка пока не подтвердила это.
0
|
||
|
1673 / 501 / 107
Регистрация: 17.05.2015
Сообщений: 1,518
|
|||||||||||
| 17.11.2023, 07:28 | |||||||||||
|
Соответственно, правило: если деструктор хочет позвать статический объект, тогда в конструкторе тоже нужно позвать этот же самый статический объект.
Локальные статические объекты имеют иммунитет к "неопределенному порядку инициализации единиц трансляций" Проблема тут может быть только в том случае, если два статических объекта перекрестно ссылаются друг на друга в конструкторах. Но так как ваша функция log никак не зависит от класса A, то проблем никаких.Пока конструктор полностью не завершил свою работу, объект считается ещё не созданным. В момент работы конструктора объекта класса Alog("") происходит в момент, когда объект A ещё не построен.Поэтому, локальный статический объект, принадлежащий функции log, гарантированно будет создан раньше, чем объект класса AА дальше вступает в силу: A, локальный статический объект функции log ещё жив, потому что он был создан раньше, чем был создан объект класса А, и следовательно гарантированно проживет дольше, чем объект класса A
2
|
|||||||||||
|
19497 / 10102 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
|
||
| 17.11.2023, 10:59 | ||
|
Суть проблемы не в порядке статической инициализации (ее вы как раз победили, разместив вызов в конструкторе, и выкладки автора выше в этом вопросе верны), а в статической деинициализации. Эту проблему как раз решают паттерны "Nifty counter" и "Phoenix singleton". Paket236, оратор выше в целом отвечал не на то, о чем я говорил. Вот у вас есть три объекта в текущей ЕТ - а, b, c, объявленные друг за другом. Все они используют log в своих деструкторах. Вы смотрите, что объект А идет первым, поэтому добавляете туда (по вашему посту №13) "создающий вызов". Далее есть другая ЕТ, где объекты идут в другом составе, например, как b, c. Тогда и в конструктор B нужно тоже будет добавить такой создающий вызов. Если этого не сделать, не смотря на то, что есть ЕТ, где порядок верный, может случиться "фиаско", но уже в деструкторе B.Т.е. таким образом абсолютно любой такой глобальный объект, где бы он не находился, должен делать такие создающие вызовы в конструкторе, что в общем-то достаточно неудобно, и требует постоянного контроля за новыми такими объектами в разных ЕТ. Поэтому я и говорю, что это решение не будет гарантированно работать из-за особенностей компиляции C++ и присутствия человеческого фактора.
1
|
||
|
1459 / 475 / 70
Регистрация: 22.09.2023
Сообщений: 1,440
|
|
| 17.11.2023, 11:03 | |
|
Вообще-то тут есть один момент, на который программисты больших компьютеров не обращают внимания:
Да, статический неконстантный объект внутри функции log создается в момент первого вызова функции. Но какой ценой это реализовано? А реализовано это в общем случае довольно просто: есть скрытый статический флаг "объект m создан", который инициализируется значением false перед вызовом конструкторов. Далее, при каждом вызове функции происходит проверка: а не равен ли этот флаг false и если равен - вызывается конструктор объекта и флаг переводится в состояние true. То есть действие, которое нужно сделать всего один раз тянет за собой код, который делает бесполезную работу при каждом вызове этой функции в течении всего остального времени жизни программы. Ну, и еще этот скрытый флаг занимает память. Не считая страдания моего чувства прекрасного можно сделать такой вывод: в функции, которая в ходе выполнения программы вызывается очень часто использование локальных нестатических объектов ведет к снижению производительности. Ну и это все относится также и к шаблону проектирования singleton, поскольку в нем точно такой же механизм реализован вручную.
1
|
|
|
19497 / 10102 / 2461
Регистрация: 30.01.2014
Сообщений: 17,808
|
|
| 17.11.2023, 11:23 | |
|
Dushevny, начиная с С++11 этот флаг еще и атомарным обязан быть, что не добавляет к упомянутому производительности
![]() И вообще я продолжаю настаивать на том, что наиболее хорошим решением будет избавление от глобальных объектов со сложной логикой в конструкторах и деструкторах совсем.
0
|
|
|
2 / 2 / 0
Регистрация: 16.08.2013
Сообщений: 86
|
|
| 17.11.2023, 11:51 [ТС] | |
|
DrOffset, спасибо за разъяснения, теперь понял вашу мысль.
![]() eva2326, Dushevny, тоже поставил плюсы за ваши комментарии.
0
|
|
| 17.11.2023, 11:51 | |
|
Помогаю со студенческими работами здесь
20
Как избежать глобальной переменной
Как получить счетчик использования переменной? Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога
Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
|
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
|
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога
В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
|
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога
Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
|
|
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога
Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
|
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога
Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
|
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования.
Часть библиотеки BedvitCOM
Использованы. . .
|
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога
SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
|