В C / C ++ глобальные переменные настолько плохи, насколько их считает мой профессор?
В C / C ++ глобальные переменные настолько плохи, насколько их считает мой профессор?
Ответы:
Проблема с глобальными переменными состоит в том, что, поскольку каждая функция имеет к ним доступ, становится все сложнее выяснить, какие функции фактически читают и записывают эти переменные.
Чтобы понять, как работает приложение, вы должны принять во внимание каждую функцию, которая изменяет глобальное состояние. Это можно сделать, но по мере роста приложения оно будет становиться все труднее и практически невозможно (или, по крайней мере, будет пустой тратой времени).
Если вы не полагаетесь на глобальные переменные, вы можете при необходимости передавать состояние между различными функциями. Таким образом, у вас гораздо больше шансов понять, что делает каждая функция, поскольку вам не нужно принимать во внимание глобальное состояние.
Важно помнить общую цель: ясность
Существует правило «без глобальных переменных», потому что большую часть времени глобальные переменные делают смысл кода менее понятным.
Однако, как и многие правила, люди помнят правило, а не то, для чего предназначалось правило.
Я видел программы, которые, кажется, удваивают размер кода, передавая огромное количество параметров, просто чтобы избежать зла глобальных переменных. В конце концов, использование глобалов сделало бы программу более понятной для тех, кто ее читает. Бездумно придерживаясь слова правила, оригинальный программист потерпел неудачу в намерении правила.
Так что да, глобалы часто плохие. Но если вы чувствуете, что в конечном итоге цель программиста становится понятнее благодаря использованию глобальных переменных, то продолжайте. Однако помните о снижении ясности, которое происходит автоматически, когда вы заставляете кого-то обращаться ко второму коду (глобальным), чтобы понять, как работает первый фрагмент.
Мой профессор говорил что-то вроде: использование глобальных переменных - это нормально, если вы используете их правильно. Я не думаю, что когда-либо умел правильно их использовать, поэтому я вообще редко их использовал.
static
часто используют глобальные переменные, язык C. Будучи ограниченным относительно небольшими единицами перевода, они начинают напоминать переменные класса объектов C ++.
program lifetime, file scope variables
. И они становятся довольно глобальными, когда вы передаете указатель на переменную во внешний мир (что невозможно с автоматическими переменными) ..
static
глобальные переменные имеют ограниченную область действия для одной и той же единицы перевода. Но у них есть время жизни до конца программы, как у любой глобальной переменной.
Глобальные переменные следует использовать только тогда, когда у вас нет альтернативы. И да, это включает в себя синглтоны. В 90% случаев глобальные переменные вводятся для экономии затрат на передачу параметров. И тогда происходит многопоточность / модульное тестирование / обслуживание кода, и у вас есть проблема.
Так что да, в 90% случаев глобальные переменные плохие. Исключения вряд ли будут замечены вами в ваши студенческие годы. Единственное исключение, которое я могу придумать, - это работа с глобальными объектами, такими как таблицы прерываний. Такие вещи, как соединение с БД, кажутся глобальными, но это не так.
Проблема, которую создают глобальные переменные для программиста, состоит в том, что он расширяет поверхность межкомпонентной связи между различными компонентами, которые используют глобальные переменные. Это означает, что по мере увеличения числа компонентов, использующих глобальную переменную, сложность взаимодействий также может увеличиваться. Такое усиленное сцепление, как правило, облегчает внедрение дефектов в систему при внесении изменений, а также затрудняет диагностику и исправление дефектов. Это увеличение связи может также уменьшить число доступных опций при внесении изменений и может увеличить усилие, требуемое для изменений, так как часто необходимо проследить через различные модули, которые также используют глобальную переменную, чтобы определить последствия изменений.
Цель инкапсуляции , которая в основном противоположна использованию глобальных переменных, состоит в том, чтобы уменьшить связь, чтобы сделать понимание и изменение источника проще, безопаснее и легче тестировать. Намного проще использовать модульное тестирование, когда глобальные переменные не используются.
Например, если у вас есть простая глобальная целочисленная переменная, которая используется в качестве перечислимого индикатора, который различные компоненты используют в качестве конечного автомата, и вы затем вносите изменение, добавляя новое состояние для нового компонента, вы должны затем проследить все остальные компоненты, чтобы гарантировать, что изменение не повлияет на них. Примером возможной проблемы может быть, если switch
оператор для проверки значения глобальной переменной перечисления с case
операторами для каждого из текущих значений используется в разных местах, и бывает, что некоторые switch
операторы не имеют default
случая для обработки Неожиданное значение для глобального внезапно вы имеете неопределенное поведение в отношении приложения.
С другой стороны, использование общей области данных может использоваться для хранения набора глобальных параметров, на которые ссылаются в приложении. Этот подход часто используется со встроенными приложениями с небольшим объемом памяти.
При использовании глобальных переменных в приложениях такого типа обычно ответственность за запись в область данных распределяется на один компонент, и все другие компоненты видят область как const
и считывают ее, а не записывают в нее. Использование этого подхода ограничивает проблемы, которые могут возникнуть.
Несколько проблем из глобальных переменных, которые нужно обойти
Когда источник для глобальной переменной, такой как структура, изменяется, все, что его использует, должно быть перекомпилировано, чтобы все, кто использует переменную, знали свой истинный размер и шаблон памяти.
Если более чем один компонент может изменять глобальную переменную, вы можете столкнуться с проблемами, связанными с несовместимыми данными в глобальной переменной. В многопоточном приложении вам, вероятно, потребуется добавить какую-либо блокировку или критическую область, чтобы обеспечить способ, чтобы только один поток за раз мог изменять глобальную переменную, а когда поток изменяет переменную, все изменения завершены и фиксируется до того, как другие потоки смогут запросить переменную или изменить ее.
Отладка многопоточного приложения, использующего глобальную переменную, может быть более сложной. Вы можете столкнуться с условиями гонки, которые могут создать дефекты, которые трудно воспроизвести. С несколькими компонентами, связывающимися через глобальную переменную, особенно в многопоточном приложении, очень трудно понять, какой компонент меняет переменную, когда и как она меняет переменную.
Конфликт имен может быть проблемой с использованием глобальных переменных. Локальная переменная, имя которой совпадает с именем глобальной переменной, может скрывать глобальную переменную. Вы также сталкиваетесь с проблемой соглашения об именах при использовании языка программирования C. Обходной путь - разделить систему на подсистемы с глобальными переменными для конкретной подсистемы, начинающимися с тех же первых трех букв (см. Это при разрешении коллизий пространства имен в задаче C ). C ++ предоставляет пространства имен, а с C вы можете обойти эту проблему, создав глобально видимую структуру, членами которой являются различные элементы данных и указатели на данные и функции, которые представлены в файле как статические, следовательно, с видимостью файла, так что на них можно ссылаться только через глобально видимая структура.
В некоторых случаях исходная цель приложения изменяется так, что глобальные переменные, которые предоставляли состояние для одного потока, модифицируются, чтобы позволить запускаться нескольким дублирующимся потокам. Примером может служить простое приложение, предназначенное для одного пользователя, использующее глобальные переменные для состояния, а затем из управления поступает запрос на добавление интерфейса REST, позволяющего удаленным приложениям выступать в качестве виртуальных пользователей. Итак, теперь вы столкнулись с необходимостью дублировать глобальные переменные и их информацию о состоянии, чтобы у одного пользователя, а также у каждого виртуального пользователя из удаленных приложений был свой собственный уникальный набор глобальных переменных.
Использование C ++ namespace
и struct
Техника для C
Для языка программирования C ++ namespace
директива является огромной помощью в уменьшении шансов столкновения имен. namespace
наряду с class
и различными ключевыми словами доступа ( private
, protected
и public
) обеспечивает большую часть инструментов, необходимые для инкапсуляции переменных. Однако язык программирования C не предоставляет эту директиву. Эта публикация потока стека, Пространства имен в C , предоставляет некоторые методы для C.
Полезный метод состоит в том, чтобы иметь одну резидентную область данных в памяти, которая определена как область, struct
которая имеет глобальную видимость, и в ней struct
есть указатели на различные глобальные переменные и функции, которые предоставляются. Фактические определения глобальных переменных задаются областью файла с использованием static
ключевого слова. Если затем вы используете const
ключевое слово, чтобы указать, какие из них доступны только для чтения, компилятор может помочь вам обеспечить доступ только для чтения.
Использование этой struct
техники также может инкапсулировать глобальное, так что оно становится своего рода пакетом или компонентом, который оказывается глобальным. Имея такой компонент, становится легче управлять изменениями, которые влияют на глобальные и функциональные возможности, используя глобальные.
Однако, хотя namespace
или struct
методика может помочь в управлении конфликтами имен, все еще существуют основные проблемы межкомпонентной связи, которые использование глобалов создает, особенно в современном многопоточном приложении.
Да, но вы не несете затрат на глобальные переменные, пока не перестанете работать в коде, который использует глобальные переменные, и не начнете писать что-то еще, которое использует код, который использует глобальные переменные. Но стоимость все еще там.
Другими словами, это долгосрочные косвенные затраты, и поэтому большинство людей считают, что это неплохо.
Если возможно, что ваш код подвергнется интенсивной проверке во время судебного разбирательства в Верховном суде , тогда вы должны избегать глобальных переменных.
См. Эту статью: Код ошибки алкотестера отражает важность исходного обзора
Были некоторые проблемы со стилем кода, которые были определены в обоих исследованиях. Одной из стилистических проблем, которые волновали рецензентов, было широкое использование незащищенных глобальных переменных . Это считается плохой формой, поскольку увеличивает риск того, что состояние программы станет несовместимым или что значения будут случайно изменены или перезаписаны. Исследователи также выразили некоторую обеспокоенность по поводу того факта, что десятичная точность не поддерживается последовательно во всем коде.
Бьюсь об заклад, те разработчики хотят, чтобы они не использовали глобальные переменные!
Глобальные переменные так же плохи, как вы их делаете, не меньше.
Если вы создаете полностью инкапсулированную программу, вы можете использовать глобальные переменные. Использовать глобальные перемены - это «грех», но грехи программирования довольно философские.
Если вы посмотрите L.in.oleum , вы увидите язык, переменные которого являются исключительно глобальными. Это не масштабируется, потому что у всех библиотек нет другого выбора, кроме как использовать глобальные переменные.
Тем не менее, если у вас есть выбор и вы можете игнорировать философию программиста, глобальные переменные не так уж и плохи.
Как и Готос, если вы используете их правильно.
Большая «плохая» проблема заключается в том, что, если вы используете их неправильно, люди кричат, разбивается марс, и мир взрывается… или что-то в этом роде.
Я бы ответил на этот вопрос другим вопросом: пользуетесь ли вы сингелтонами / плохо ли сингелтон?
Потому что (почти все) использование сингелтона является прославленной глобальной переменной.
Проблема не в том, что они плохие , а в том, что они опасны . У них есть свои плюсы и минусы, и бывают ситуации, когда они являются либо наиболее эффективным, либо единственным способом решения конкретной задачи. Тем не менее, ими очень легко злоупотреблять, даже если вы предпринимаете шаги, чтобы всегда их правильно использовать.
Несколько плюсов:
Несколько минусов:
Заметьте, если хотите, что первые два плюса и первые два минуса, которые я перечислил, - это одно и то же, только с другой формулировкой. Это связано с тем, что возможности глобальной переменной действительно могут быть полезны, но именно те функции, которые делают их полезными, являются источником всех их проблем.
Несколько потенциальных решений некоторых проблем:
Globals
или GlobalVars
), или используйте стандартное соглашение об именах для глобальных переменных (таких как global_[name]
или g_module_varNameStyle
(как упомянуто underscore_d в комментариях). )). Это будет как документировать их использование (вы можете найти код, который использует глобальные переменные, выполняя поиск по пространству имен / имени структуры), так и минимизировать влияние на глобальное пространство имен.extern
в связанном заголовке, чтобы их использование могло быть ограничено модулями компиляции, которым необходим доступ к ним. Если ваш код основан на большом количестве глобальных переменных, но для каждого модуля компиляции требуется доступ только к нескольким из них, вы можете рассмотреть возможность сортировки их по нескольким исходным файлам, чтобы было проще ограничить доступ каждого файла к глобальным переменным.Хорошие они или плохие, зависит от того, как вы их используете. Большинство склонны использовать их плохо, отсюда и общая настороженность по отношению к ним. При правильном использовании они могут быть главным благом; если используется плохо, однако, они могут и будут возвращаться , чтобы укусить вас , когда и как вы меньше всего этого ожидаете.
Хороший способ взглянуть на это состоит в том, что сами они неплохие, но они допускают плохой дизайн и могут экспоненциально умножать эффекты плохого дизайна.
Даже если вы не собираетесь их использовать, лучше знать, как их использовать безопасно, и выбирать не использовать, чем не использовать их, потому что вы не знаете, как их использовать безопасно. Если вы когда-нибудь окажетесь в ситуации, когда вам нужно поддерживать уже существующий код, основанный на глобальных переменных, у вас могут возникнуть трудности, если вы не знаете, как их правильно использовать.
g_module_varNameStyle
совершенно разборчивым. Чтобы быть ясным, я не использую глобальные переменные, если я могу легко избежать их - ключевое слово легко , потому что, поскольку я перестал верить, что их нужно избегать - или, скорее, запутать - любой ценой, я провожу намного лучше, и мои код (шок!) намного опрятнее
Как кто-то сказал (я перефразирую) в другой теме: «Подобные правила не должны нарушаться, пока вы полностью не поймете последствия этого».
Бывают ситуации, когда необходимы глобальные переменные или, по крайней мере, они очень полезны (например, работа с системными обратными вызовами). С другой стороны, они также очень опасны по всем причинам, которые вам сказали.
Существует много аспектов программирования, которые, вероятно, следует оставить экспертам. Иногда вам нужен очень острый нож. Но вы не можете использовать один, пока не будете готовы ...
Глобальные переменные, как правило, плохие, особенно если другие люди работают над тем же кодом и не хотят тратить 20 минут на поиск всех мест, на которые ссылается переменная. И добавление потоков, которые изменяют переменные, приносит совершенно новый уровень головной боли.
Глобальные константы в анонимном пространстве имен, используемые в одном модуле перевода, хороши и распространены в профессиональных приложениях и библиотеках. Но если данные являются изменяемыми, и / или они должны быть разделены между несколькими TU, вы можете захотеть инкапсулировать их - если не ради дизайна, то ради кого-либо отладки или работы с вашим кодом.
Использование глобальных переменных - это что-то вроде грязи под ковриком. Это быстрое решение, и в краткосрочной перспективе намного проще, чем получить пылесборник или пылесос для его очистки. Однако, если вы когда-нибудь в конечном итоге будете двигать ковер, у вас будет большой сюрприз под ним.
Я думаю, что ваш профессор пытается избавиться от вредной привычки еще до того, как она начнется.
Глобальные переменные имеют свое место, и, как говорили многие люди, знание, где и когда их использовать, может быть сложным. Так что я думаю, а не вдаваться в подробности того, почему, как, когда и где глобальные переменные ваш профессор решил просто запретить. Кто знает, он может отменить их в будущем.
Точно нет. Хотя злоупотреблять ими ... это плохо.
Бессмысленно удалять их ради всего лишь ... бездумно. Если вы не знаете о преимуществах и недостатках, лучше держаться подальше и действовать так, как вас учили / изучали, но в глобальных переменных нет ничего неявно неправильного. Когда вы понимаете плюсы и минусы, лучше примите собственное решение.
Глобальные переменные хороши в небольших программах, но ужасны, если используются в больших программах тем же способом.
Это означает, что вы можете легко привыкнуть использовать их во время обучения. Это то, от чего ваш профессор пытается вас защитить.
Когда вы станете более опытным, вам будет легче учиться, когда они в порядке.
Нет, они совсем не плохие. Вам нужно взглянуть на (машинный) код, созданный компилятором, чтобы сделать это определение, иногда гораздо хуже использовать локальный, чем глобальный. Также обратите внимание, что добавление «static» в локальную переменную в основном делает ее глобальной (и создает другие уродливые проблемы, которые реальный глобал мог бы решить). "местные глобалы" особенно плохи.
Глобальные переменные дают вам полный контроль над использованием памяти, что гораздо сложнее сделать с местными жителями. В наши дни это имеет значение только во встроенных средах, где память довольно ограничена. Что-то, что нужно знать, прежде чем предполагать, что встроенные - то же самое, что и другие среды, и предположить, что правила программирования одинаковы для всех.
Хорошо, что вы ставите под сомнение изучаемые правила, большинство из них не по тем причинам, о которых вам говорят. Однако самый важный урок заключается не в том, что это правило, которое нужно носить с собой вечно, но это правило, которое необходимо соблюдать, чтобы пройти этот класс и двигаться вперед. В жизни вы обнаружите, что для компании XYZ у вас будут другие правила программирования, которые вам, в конце концов, придется соблюдать, чтобы продолжать получать зарплату. В обеих ситуациях вы можете утверждать правило, но я думаю, что вам повезет больше на работе, чем в школе. Вы просто еще один из многих студентов, ваше место скоро будет заменено, профессора не будут, на работе вы являетесь одной из небольшой команды игроков, которые должны довести этот продукт до конца, и в этой среде разработанные правила предназначены для благо членов команды, а также продукта и компании, поэтому, если у всех есть единомышленники или если для конкретного продукта есть веская техническая причина нарушить то, что вы узнали в колледже или какую-то книгу по родовому программированию, продайте свою идею команде и запишите ее как действительный, если не предпочтительный метод , Все в честной игре в реальном мире.
Если вы будете следовать всем правилам программирования, которым вас учат в школе или книгах, ваша карьера программиста будет крайне ограничена. Вероятно, вы сможете выжить и иметь плодотворную карьеру, но широта и ширина доступных вам сред будут крайне ограничены. Если вы знаете, как и почему существует правило и можете его защитить, это хорошо, если вы только рассуждаете «потому что так сказал мой учитель», ну, это не так хорошо.
Обратите внимание, что подобные темы часто обсуждаются на рабочем месте и будут продолжать развиваться по мере развития компиляторов и процессоров (и языков), так же как и правила такого рода, без защиты вашей позиции и, возможно, обучения кого-то другого, с другим мнением, которое вы не примете. двигаться вперед.
А пока просто делайте то, что говорит тот, кто говорит громче всего или несет самую большую палку (до тех пор, пока вы не будете кричать громко и нести самую большую палку).
Я бы хотел возразить против того, что в этой теме делается вывод о том, что многопоточность усложняется или невозможна сама по себе. Глобальные переменные являются общим состоянием, но альтернативы глобальным переменным (например, передача указателей) также могут иметь общее состояние. Проблема с многопоточностью заключается в том, как правильно использовать общее состояние, а не в том, является ли это состояние общим через глобальную переменную или что-то еще.
Большую часть времени, когда вы выполняете многопоточность, вам нужно делиться чем-то. Например, в шаблоне «производитель-потребитель» вы можете использовать некоторую потокобезопасную очередь, содержащую рабочие блоки. И вам разрешено делиться этим, потому что эта структура данных является поточно-ориентированной. Является ли эта очередь глобальной или нет, совершенно не имеет значения, когда речь заходит о безопасности потоков.
Подразумеваемая надежда, выраженная в этом потоке, что преобразование программы из однопоточной в многопоточную будет проще, если не использовать глобальные переменные, наивно. Да, глобалы помогают стрелять себе в ногу, но есть много способов выстрелить себе.
Я не защищаю глобальные переменные, так как другие точки все еще остаются в силе, моя точка зрения заключается просто в том, что количество потоков в программе не имеет ничего общего с переменной областью действия.
Да, потому что, если вы позволите некомпетентным программистам использовать их (читайте 90%, особенно ученых), вы получите более 600 глобальных переменных, разбросанных по более чем 20 файлам, и проект из 12 000 строк, где 80% функций отменяют, возвращают недействительные и работают полностью в глобальном состоянии.
Быстро становится невозможно понять, что происходит в любой момент, если вы не знаете весь проект.
Использование глобальных переменных на самом деле зависит от требований. Его преимущество заключается в том, что он уменьшает накладные расходы на повторную передачу значений.
Но ваш профессор прав, потому что это поднимает вопросы безопасности, поэтому следует как можно больше избегать использования глобальных переменных. Глобальные переменные также создают проблемы, которые иногда трудно отладить .
Например:-
Ситуации, когда значения переменных изменяются во время выполнения . В этот момент сложно определить, какая часть кода модифицирует его и на каких условиях.
Глобальные хороши, когда дело доходит до конфигурации . Когда мы хотим, чтобы наша конфигурация / изменения имели глобальное влияние на весь проект .
Таким образом, мы можем изменить одну конфигурацию, и изменения будут направлены на весь проект . Но я должен предупредить, что вы должны быть очень умны, чтобы использовать глобальные переменные.
Рано или поздно вам нужно будет изменить способ установки этой переменной или то, что происходит при обращении к ней, или вам просто нужно найти место, где она была изменена.
Практически всегда лучше не иметь глобальных переменных. Просто напишите методы получения и установки плотины и будьте готовы помочь вам, когда они понадобятся вам через день, неделю или месяц.
Я обычно использую глобальные переменные для значений, которые редко меняются, например, синглтоны или указатели на функции в динамически загружаемой библиотеке. Использование изменяемых глобальных переменных в многопоточных приложениях приводит к трудным отслеживанию ошибок, поэтому я стараюсь избегать этого как общего правила.
Использование глобального вместо передачи аргумента часто быстрее, но если вы пишете многопоточное приложение, что вы часто делаете в настоящее время, оно обычно работает не очень хорошо (вы можете использовать статику потоков, но тогда выигрыш в производительности сомнителен) ,
В веб-приложениях внутри предприятия можно использовать для хранения данных сессии / окна / потока / пользователя на сервере по причинам оптимизации и для защиты от потери работы, когда соединение нестабильно. Как уже упоминалось, условия гонки должны быть обработаны. Мы используем один экземпляр класса для этой информации, и он тщательно управляется.
В конце концов, ваша программа или приложение все еще могут работать, но это вопрос аккуратности и полного понимания происходящего. Если вы разделяете значение переменной среди всех функций, может оказаться трудным отследить, какая функция меняет значение (если функция делает это) и усложняет отладку в миллион раз
безопасность меньше, значит любой может манипулировать переменными, если они объявлены глобальными, для объяснения этого возьмем этот пример, если у вас есть баланс в качестве глобальной переменной в вашей банковской программе, пользовательская функция может манипулировать этим, а также банковский служащий может также манипулировать В этом и заключается проблема. Только пользователю должна быть предоставлена функция «только чтение» и «снятие», но клерк банка может добавить сумму, когда пользователь лично отдает деньги на стойке регистрации. Так оно и работает.
В многопоточном приложении вместо локальных переменных используйте локальные переменные, чтобы избежать состояния гонки.
Состояние гонки возникает, когда несколько потоков обращаются к общему ресурсу, по крайней мере, один поток имеет доступ на запись к данным. Тогда результат программы не предсказуем и зависит от порядка доступа к данным различными потоками.
Подробнее об этом здесь, https://software.intel.com/en-us/articles/use-intel-parallel-inspector-to-find-race-conditions-in-openmp-based-multithreaded-code