- Урок №50. Почему глобальные переменные – зло?
- Почему (неконстантные) глобальные переменные — это зло?
- В чём плюсы использования (неконстантных) глобальных переменных?
- Как защититься от «глобального разрушения»?
- как исключить использование глобальной переменной
- 2 ответа
- Правильная структура программы. Избавление от глобальной переменной
- 1 ответ 1
- Статические переменные вместо глобальных на Си/Си++
- Лига программистов C/C++
- Правила сообщества
- Пятнашки и А*
Урок №50. Почему глобальные переменные – зло?
Обновл. 11 Сен 2020 |
Если вы попросите ветерана-программиста дать один дельный совет о программировании, то после некоторого раздумья он ответит: «Избегайте использования глобальных переменных!». И, частично, он будет прав. Глобальные переменные являются одними из самых злоупотребляемых объектов в языке C++. Хоть они и выглядят безвредными в небольших программах, использование их в крупных проектах зачастую чрезвычайно проблематично.
Новички часто используют огромное количество глобальных переменных, потому что с ними легко работать, особенно когда задействовано много функций. Это плохая идея. Многие разработчики считают, что неконстантные глобальные переменные вообще не следует использовать!
Но прежде, чем мы разберемся с вопросом «Почему?», нужно кое-что уточнить. Когда разработчики говорят, что глобальные переменные — это зло, они НЕ подразумевают полностью ВСЕ глобальные переменные. Они говорят о неконстантных глобальных переменных.
Почему (неконстантные) глобальные переменные — это зло?
Безусловно, причина №1, почему неконстантные глобальные переменные являются опасными, — это то, что их значения могут изменять любые вызываемые функции, при этом вы можете этого и не знать. Например, рассмотрим следующую программу:
Результат выполнения программы:
Launching nuclear missiles.
Сначала мы присваиваем переменной g_mode значение 1 , а затем вызываем функцию doSomething(). Если бы мы не знали заранее, что doSomething() изменит значение g_mode , то, вероятно, не ожидали бы дальнейшего развития событий ( g_mode = 2 => Launching nuclear missiles. )!
Неконстантные глобальные переменные делают каждую функцию потенциально опасной, и программист не может заранее знать, какая из используемых им функций является опасной, а какая — нет. Локальные переменные намного безопаснее, потому что другие функции не могут влиять на них напрямую.
Также есть много других веских причин не использовать неконстантные глобальные переменные. Например, нередко можно встретить примерно следующее:
Предположим, что g_mode равно 3 , а не 4 — наша программа выдаст неверные результаты. Как это исправить? Нужно будет отыскать все места, где предположительно могло измениться значение переменной g_mode , а затем проследить ход выполнения кода в каждом потенциально опасном участке. Возможно, изменение глобальной переменной вы обнаружите вообще в другом коде, который, как вам казалось на первый взгляд, никак не связан с фрагментом, приведенным выше.
Одной из причин объявления локальных переменных максимально близко к месту их первого использования является уменьшение количества кода, которое нужно будет просмотреть, чтобы понять, что делает (зачем нужна?) переменная. С глобальными переменными дела обстоят несколько иначе — поскольку их можно использовать в любом месте программы, то вам придется просмотреть чуть ли не весь код, чтобы проследить логику выполнения и изменения значений переменных в вашей программе.
Например, вы можете обнаружить, что на g_mode ссылаются 442 раза в вашей программе. Если использования переменной g_mode не подкреплены комментариями, то вам придется просмотреть каждое упоминание g_mode , чтобы понять, как оно используется в разных случаях.
Также глобальные переменные делают вашу программу менее модульной и гибкой. Функция, которая использует только свои параметры и не имеет побочных эффектов, является идеальной в плане модульности. Модульность помогает понять структуру вашей программы, что она делает и как можно повторно использовать определенные участки кода в другой программе. Глобальные переменные значительно уменьшают эту возможность.
В частности, не используйте глобальные переменные в качестве важных переменных, которые выполняют главные или решающие функции в программе (например, переменные, которые используются в условных стейтментах, как g_mode выше). Ваша программа вряд ли сломается, если в ней будет глобальная переменная с информационным значением, которое может меняться (например, имя пользователя). Гораздо хуже, если изменится значение глобальной переменной, которая влияет непосредственно на результаты выполнения самой программы или на её работу.
Правило: Вместо глобальных переменных используйте локальные (когда это целесообразно).
В чём плюсы использования (неконстантных) глобальных переменных?
Их немного. Зачастую можно обойтись без использования неконстантных глобальных переменных. Но в некоторых случаях их разумное использование поможет уменьшить сложность программы, и, иногда, может быть даже лучшим вариантом для решения проблемы, чем альтернативные способы.
Например, если ваша программа использует базу данных для чтения и записи данных, то имеет смысл определить базу данных глобально, поскольку доступ к ней может понадобиться с любого места. Аналогично, если в вашей программе есть журнал ошибок (или журнал отладки), в котором вы можете читать/записывать информацию об ошибках (или об отладке), то имеет смысл определить его глобально. Звуковая библиотека может быть еще одним хорошим примером: вам, вероятно, не захочется открывать доступ к ней для каждой функции, которая делает запрос. Поскольку у вас будет только одна звуковая библиотека, управляющая всеми звуками, логично будет объявить её глобально, инициализировать при запуске программы, а затем использовать только в режиме чтения.
Как защититься от «глобального разрушения»?
Если у вас возникнет ситуация, где полезнее будет использовать неконстантные глобальные переменные, вместо локальных, то вот вам несколько полезных советов, которые помогут свести к минимуму количество потенциальных проблем, с которыми вы можете столкнуться при использовании подобных переменных.
Во-первых, добавляйте префикс g_ ко всем вашим глобальным переменным и/или размещайте их в пространстве имен, дабы уменьшить вероятность возникновения конфликтов имен.
Источник
как исключить использование глобальной переменной
Я использую простую библиотеку json во встроенной среде и пытаюсь избавиться от глобальной переменной в своем пользовательском коде. У меня есть фрагмент, который пытается объяснить мою ситуацию.
В библиотеке есть функция libjson_parser , которую можно вызывать каждый раз, когда необходимо проанализировать массив символов. По завершении операции она передает результат операции функции, которая зарегистрирована как обратный вызов ( json_post_parse в следующем примере), используя libjson_callback_register .
Поскольку у меня есть разные типы json-пакетов для анализа, я использую глобальную переменную parsing_config , которая устанавливается перед вызовом синтаксического анализатора, чтобы в функции json_post_parse можно было выполнить правильную операцию.
Я знаю, что использование глобальных переменных неодобрительно, поэтому я ищу способы избавиться от этой глобальной переменной, но не знаю, как это сделать?
2 ответа
Вероятно, вам следует динамически изменить обратный вызов, как предложено @CharlieBurns, но альтернативой может быть скрытие статики в функции get / set:
В качестве альтернативы (и более условно) вы можете поместить json_post_parse(json_obj* json) в отдельную единицу перевода с помощью функции общего доступа. Переменная по-прежнему имеет область видимости файла, но она видна только тем функциям, к которым у бизнеса есть доступ. Область видимости файла не является глобальной областью, и пока переменная не видна функциям, которым не нужно ее видеть, что позволяет избежать проблем, связанных с глобальными переменными. Так:
Источник
Правильная структура программы. Избавление от глобальной переменной
Проблема следующая. Я правлю баг, суть которого в невозможности завершения программы. Т.е. управление командной строке не возвращается.
Я взял пример кода от сюда (мы используем этот фрэймворк)
Код изменил, чтобы продемонстрировать свой случай:
Проблема в переменной myProxy . Она, как я понял, запускает thread при вызове метода buildProxy . Из-за того, что переменная глобальная, поток не убивается правильно. Даже если завершится main() , программа не остановится.
Дополнительную проблему создает то, что в программе содержится структура с методом, который содержит myProxy .
Так что довольно сложно избавиться от это глобальной переменной без существенных переделок.
Решение я нашел пока только одно — сделать ее локальной. Тогда все работает нормально.
Но решение получилось не очень красивое. Подскажите, как это сделать правильнее? Также мне желательно оставить void helpFunction() иначе придется много переделывать.
1 ответ 1
«Из-за того, что переменная глобальная, поток не убивается правильно. Даже если завершится main(), программа не остановится.»
- Нет, не так. При завершении main все потоки завершаются. Какие-то переменные вообще к делу не относятся. Если программа у вас не завершается, значит главный поток по-прежнему активен, это единственная причина.
«Решение я нашел пока только одно — сделать ее локальной. Тогда все работает нормально. . Но решение получилось не очень красивое. Подскажите, как это сделать правильнее?»
- Уж точно не очень красивое. 🙂 А правильнее было бы разобраться из-за чего у вас подвисает и не завершается главный поток. Принудительно его можно завершить, например, функцией abort или terminate, но лучше все-таки понять что происходит, и нет ли там еще каких нежелательных эффектов. Сделайте трассировку выполнения в текстовый лог файл, очень полезная вещь для отладки многопоточных программ. Отладчик тут не всегда помогает, а лог выполнения хорошо показывает как взаимодействуют потоки.
Источник
Статические переменные вместо глобальных на Си/Си++
Есть простой способ избавиться от большинства глобальных переменных.
Переменная A располагается в глобальной области памяти, но имеет локальную область видимости (в пределах функции fun). То есть переменная сохраняет свое значение между вызовами функции. Начальное значение переменной равно 10.
Этот способ не годится, если функциям нужно обмениваться данными, например, обработчику прерывания с основной программой. Это решается созданием глобальной переменной в отдельном файле. Ниже приведен код такого файла.
Область видимости переменной A — только этот файл. Никаким способом к ней нельзя получить доступ из других файлов. Функции setA и getA позволяют получить и установить значение переменной.
На Си++ переменную дополнительно можно обернуть классом.
Лига программистов C/C++
43 поста 4.2K подписчиков
Правила сообщества
Соблюдайте правила Pikabu:
Помимо этого ЗАПРЕЩЕНО:
— Размещать в сообществе посты стиля «Подскажите как стать программистом», «Подскажите как удалить вирус», «Подскажите как установить программу», «Подскажите как починить монитор/телевизор/мышь/тостер/клавиатуру» или «Напишите за меня лабу в универ». Пожалуйста размещайте такие посты вне этого сообщества или в соответствующих для этого сообществах.
Ммм. функции с неявными сайд-эффектами, какая прелесть)
С точки зрения практик кодирования, нет никакой принципиальной разницы между вашей «статичной переменной» и классической глобальной.
На ревью кода программисты в нашей компании одинаково напишут коммент «глобальная переменная».
Хоть она static в корневом скоупе, хоть она завернута в анонимный namespace, хоть объявлена static полем класса. Если это не константа — на ревью это будет «глобальная переменная» и от нее надо будет отбрехиваться)
Да, есть преимущество в том что не видно из других модулей — но это мелочь, это такая же плохая практика кодирования.
Ну и чем плохи глобальные переменные?
Если вам вдруг понадобился static, то что то в архитектуре приложения пошло не так.
Это обычно самый простой, но самый плохой способ.
Как-то автор очень резко отбросил фразу: «. потому что создает много проблем.» и даже не перечислил, к каким, по его мнению, проблемам это может привести.
А вот мне было интересно послушать его мнение на это.
Это конечно всё интересно, но причём здесь пикабу?
Область видимости переменной A — только этот файл. Никаким способом к ней нельзя получить доступ из других файлов.
Можно и называется это extern и именно extern разбивает в пух и прах весь ваш «гайд». Так же предложенное вами решение к обращению к переменной не решает проблемы, которую вы сами же указали в виде ссылки на вики, а именно 2 и более потока с общей памятью
ну да, ведь памяти уже ставят гигабайты.
где наши 90-е
Это какой же компилятор си и си++ не преобразует переменную в статическую, если та объявлена внутри функции?
Пятнашки и А*
Навеяно постом Собеседование vs первый рабочий день и комментами к нему.
Ходил на курсы С++ для общего развития. Работа с программированием никак не связана, но мечтал с юности об этом деле, а возможности небыло. Подрос немного до 30+, начал нормально зарабатывать и решил воплощать мечту. Был самый старший в группе, годиков на десять от всех.
Так вот. На курсах была задачка написать игру Пятнашки с примитивным интерфейсом. Из функционала — только клик по квадратикам с цифрами, их перемещение и распознавание победной комбинации.
Преподаватель решил немного нас подзадорить и сказал, кто напишет автосборку из случайной раскладки, того САМ лично за руку поведёт на собеседование. Описал он эту задачу, как не воплотимую для таких простых как мы.
Вобщем-то, зацепил за живое.
Не боги же горшки лепят, подумал я. И погряз на 2 месяца в изучение темы и написание кода. Информации в интернете было очень мало. Можно сказать и небыло вовсе. Пример кода посмотреть не у кого было. На каком то сайте нашёл чью то работу с олимпиады, где математичесски оценивались разные варианты применения разных алгоритмов для решения этой задачи. Вот тут то я и познакомился с А* )))
Что бы понять его на интуитивном уровне ушло ещё недели две. Но когда пазл в голове сложился в чёткую картинку я почувствовал настоящую эйфорию. Код просто рекой полился. Автосборка была написана!
Я был больше чем просто рад, я кайфовал ежесекундно!
На очередном занятии показал результат преподавателю. Тот открыл ехешник, посмотрел на интерфейс и сказал, что сейчас покажет как это должно выглядеть. Открыл чей то проект, бесспорно визуально более красивый, и начал хвалить его автора — бывшего своего студента. Рассказывал какой он был распрекрасный чувак и как много добился на сегодня. Я попросил запустить автосборку чужого проекта. С рандомной раскладки решение было найдено за 140 с чем то ходов. И тут я понял, что его вариант находит не самый короткий верный путь, а просто первый попавшийся верный, на что, собственно я и указал, попросив запустить эту же раскладку в моём варианте. Моя програма дошла до финиша за 40 с чем то ходов. Я был так горд собой, что это отчётливо читалось на моём лице. Но преподаватель не поверил. Попросил оставить исходники, что бы он всё перепроверил. Так и проверяет до сих пор. А мне по человечески было реально обидно, что он не сдержал слово и не предложил идти на собеседование. Я бы отказался, так как работа у меня нормальная, хоть и не в IT, но за державу обидно.
Доверие к себе он подорвал ровно в тот момент не только у меня, но и у всей группы.
Источник