Функции объявленные как static внутри

12.14 – Статические функции-члены

В предыдущем уроке (12.13 – Статические переменные-члены) вы узнали, что статические переменные-члены являются переменными-членами, которые принадлежат классу, а не объектам класса. Если статические переменные-члены являются открытыми, мы можем получить к ним доступ напрямую, используя имя класса и оператор разрешения области видимости. Но что, если статические переменные-члены являются закрытыми? Рассмотрим следующий пример:

В этом случае мы не можем получить доступ к Something::s_value напрямую из main() , потому что это закрытая переменная-член. Обычно мы получаем доступ к закрытым членам через открытые функции-члены. Хотя мы могли бы создать обычную открытую функцию-член для доступа к s_value , но тогда нам нужно было бы создать экземпляр объекта класса, чтобы использовать эту функцию! Мы можем добиться большего. Оказывается, мы также можем сделать статическими и функции.

Как и статические переменные-члены, статические функции-члены не привязаны к какому-либо конкретному объекту. Вот приведенный выше пример со статической функцией-членом доступа:

Поскольку статические функции-члены не прикреплены к конкретному объекту, их можно вызывать напрямую, используя имя класса и оператор разрешения области видимости. Как и статические переменные-члены, они также могут вызываться через объекты класса, хотя это не рекомендуется.

У статических функций-членов нет указателя *this

У статических функций-членов есть две интересные особенности, на которые стоит обратить внимание. Во-первых, поскольку статические функции-члены не прикреплены к объекту, у них нет указателя this ! Это должно быть понятно, если подумать – указатель this всегда указывает на объект, над которым работает функция-член. Статические функции-члены не работают с объектом, поэтому указатель this не требуется.

Во-вторых, статические функции-члены могут напрямую обращаться к другим статическим членам (переменным или функциям), но не к нестатическим членам. Это связано с тем, что нестатические члены должны принадлежать объекту класса, а у статических функций-членов нет объекта класса, с которым можно было бы работать!

Еще один пример

Статические функции-члены также могут быть определены вне объявления класса. Это работает так же, как и для обычных функций-членов.

Эта программа печатает:

Обратите внимание: поскольку все данные и функции в этом классе статические, нам не нужно создавать экземпляр объекта класса, чтобы использовать его функциональные возможности! Этот класс использует статическую переменную-член для хранения значения следующего ID, который будет назначен, и предоставляет статическую функцию-член для возврата этого ID и его увеличения.

Предупреждение о классах со всеми статическими членами

Будьте осторожны при написании классов со всеми статическими членами. Хотя такие «чисто статические классы» (также называемые «моносостояниями») могут быть полезны, они также имеют некоторые потенциальные недостатки.

Во-первых, поскольку все статические члены создаются только один раз, невозможно создать несколько копий чистого статического класса (без клонирования класса и его переименования). Например, если вам нужны два независимых объекта IDGenerator , это невозможно с одним чисто статическим классом.

Во-вторых, из урока, посвященного глобальным переменным, вы узнали, что глобальные переменные опасны, потому что любой фрагмент кода может изменить значение глобальной переменной и в конечном итоге сломать другой, казалось бы, несвязанный фрагмент кода. То же самое верно и для чисто статических классов. Поскольку все члены принадлежат классу (а не объекту класса), а объявления классов обычно имеют глобальную область видимости, чисто статический класс, по сути, эквивалентен объявлению функций и глобальных переменных в глобально доступном пространстве имен со всеми недостатками, присущими глобальным переменным.

C++ не поддерживает статические конструкторы

Если вы можете инициализировать обычные переменные-члены с помощью конструктора, то, экстраполируя эту мысль, имеет смысл иметь возможность инициализировать статические переменные-члены с помощью статического конструктора. И хотя некоторые современные языки поддерживают статические конструкторы именно для этой цели, C++, к сожалению, не входит в их число.

Если ваша статическая переменная может быть инициализирована напрямую, конструктор не требуется: вы можете инициализировать статическую переменную-член в точке определения (даже если она является закрытой). Мы делаем это в приведенном выше примере IDGenerator . Вот еще один пример:

Если для инициализации вашей статической переменной-члена требуется выполнение кода (например, цикла), существует множество различных, несколько странных способов сделать это. Один из способов, который работает со всеми переменными, статическими или нет, – это использовать лямбду и немедленно вызывать ее.

Следующий код представляет метод, который больше похож на обычный конструктор. Однако он немного сложнее, и, вероятно, вам это никогда не понадобится, поэтому, если хотите, можете пропустить оставшуюся часть этого раздела.

Когда определяется статический член s_initializer , будет вызываться конструктор по умолчанию init_static() (поскольку s_initializer имеет тип init_static ). Мы можем использовать этот конструктор для инициализации любых статических переменных-членов. Хороший момент в этом решении заключается в том, что весь код инициализации скрыт внутри исходного класса со статическим членом.

Резюме

Статические функции-члены в классе можно использовать для работы со статическими переменными-членами. Их не требуется вызывать именно через объект класса.

Можно создавать классы со всеми статическими переменными-членами и статическими функциями. Однако такие классы, по сути, эквивалентны объявлению функций и глобальных переменных в глобально доступном пространстве имен, и их обычно следует избегать, если у вас нет особо веских причин для их использования.

Русские Блоги

При программировании на Objective-C я иногда использую некоторые функции языка C. Четыре ключевых слова extern, static, const и inline — это четыре ключевых слова, которые у меня расплывчатые значения. Я буду использовать их для заметки крупным планом. Приходите просмотреть это.

extern

Чтобы понять роль ключевого слова extern, вы должны сначала понять разницу между объявлением и определением. Когда переменная определена, компилятор выделяет память для переменной и может также инициализировать ее содержимое определенным значением. При объявлении переменных компилятор требует, чтобы переменные были определены в другом месте. Объявление сообщает компилятору, что существует переменная с таким именем и типом, но компилятору не нужно выделять для нее память, потому что она размещена где-то еще. Ключевое слово extern означает «объявить без определения». Другими словами, это метод явного объявления переменных или обязательных объявлений без определения.

Переменная должна быть точно определена в модуле программы. Если определения нет или существует более одного определения, на этапе связывания могут возникнуть ошибки. Переменные можно объявлять несколько раз, если объявления согласуются друг с другом и с определением (файлы заголовков полезны). Он может быть объявлен во многих модулях, включая модуль, который его определяет, или даже несколько раз в одном и том же модуле. Но обычно бессмысленно объявлять его несколько раз в модуле.

Внешние переменные также могут быть объявлены внутри функций. В этом случае необходимо использовать ключевое слово extern, иначе компилятор будет рассматривать его как определение локальной (автоматической) переменной, которая имеет другую область видимости, жизненный цикл и начальное значение. Это объявление видно только внутри функции, а не во всем функциональном модуле.

Ключевое слово extern, примененное к прототипу функции, не имеет абсолютно ничего (ключевое слово extern, примененное к определению функции, конечно, не является семантическим). Прототип функции — это всегда объявление, а не определение. Кроме того, в стандарте C функции всегда являются внешними, но некоторые расширения компилятора позволяют определять функции внутри функций.

Example:

В этом примере переменная Global_Variable определена в файле 1. Чтобы использовать ту же переменную в файле 2, ее необходимо объявить. Независимо от количества файлов глобальная переменная определяется только один раз; однако она должна быть объявлена ​​в любом файле, кроме файла, содержащего определение.

Обычный метод заключается в размещении и фактическом определении в файле .c, но не выделяются только объявление и прототип, описываются только тип и параметры, чтобы компилятор мог нормально работать, а информация принадлежит файлу заголовка .h , другие могут безопасно включать никаких возможных конфликтов.

static

В языке программирования C (и его близких потомках, таких как C ++ и Objective-C) static — это зарезервированное слово, используемое для управления жизненным циклом (как статическая переменная) и видимостью (в зависимости от ссылки).

Ключевое слово static, используемое в качестве префикса при объявлении переменной или функции, может иметь другие эффекты в зависимости от того, где происходит объявление.

Static global variable

Переменные, объявленные как статические на верхнем уровне исходного файла (вне каких-либо определений функций), видны только во всем файле. В этом случае ключевое слово static называется "спецификатором доступа".

Static function

Точно так же статические функции-функции, объявленные как статические на верхнем уровне исходного файла (вне каких-либо определений классов), видны только во всем файле.

Static local variables

Переменные, объявленные как статические внутри функции, выделяются статически, поэтому их блок памяти поддерживается в течение всего выполнения программы, имея тот же видимый диапазон, что и автоматические локальные переменные (авто и регистр), что означает сохранение локальности функции. Следовательно, любое значение, которое функция помещает в свою статическую локальную переменную при однократном вызове, все еще существует при повторном вызове функции.

const

В языке C const является частью типа, а не частью объекта. Например, в C const int x = 1; объявляет, что объект типа const int x-const является частью этого типа, как если бы он был проанализирован как "(const int) x"

Это имеет два тонких последствия. Во-первых, const можно применять к более сложным типам — например, int const * const x; объявляет постоянный указатель на постоянное целое число, а int const * x; объявляет указатель переменной на постоянное целое число, а int * const x; Объявите постоянный указатель на переменную целое число. Во-вторых, поскольку const является частью типа, она должна соответствовать как часть проверки типа.

Хотя константы не меняют своих значений во время работы программы, объекты, объявленные как const, действительно могут изменять свои значения во время работы программы. Типичный пример — регистры только для чтения во встроенных системах, например, текущее состояние цифровых входов. Регистры цифровых входных данных обычно объявляются как постоянные и изменчивые. Содержимое этих регистров может измениться, когда программа выполняет какую-либо операцию (volatile), но вы не должны записывать в них (const).

Как пользоваться

В C все типы данных (включая определяемые пользователем типы данных) могут быть объявлены как константы, а корректность констант требует, чтобы все переменные или объекты были объявлены как таковые, если их не нужно изменять. Такое активное использование const делает значение «более простым для понимания, отслеживания и рассуждений», тем самым улучшая читаемость и понятность кода, облегчая командную работу и поддержку кода, поскольку оно передает предполагаемое использование значения Information. Это может помочь компиляторам и разработчикам в размышлениях о коде. Это также может позволить оптимизирующему компилятору генерировать более эффективный код.

Простой тип данных

Для простых типов данных без указателя применить квалификатор const просто. По историческим причинам он может появляться на любой стороне типа (т. Е. Const char foo = ‘a’; эквивалентно char const foo = ‘a’;). В некоторых реализациях использование const с обеих сторон типа (например, const char const) будет генерировать предупреждения, но не ошибки.

Указатели и ссылки

Для указателей и ссылочных типов значение const более сложное — сам указатель или значение, на которое указывает, или оба могут быть const. Кроме того, синтаксис может сбивать с толку. Указатель может быть объявлен как константный указатель на записываемое значение, как записываемый указатель на константное значение, или как константный указатель на константное значение. Константный указатель нельзя переназначить, чтобы он указывал на объект, отличный от первоначально выделенного, но его можно использовать для изменения значения, на которое он указывает (называемого объектом-указателем). Следовательно, ссылочные переменные являются альтернативным синтаксисом для константных указателей. С другой стороны, указатель на константный объект может быть перераспределен, чтобы указать на другое место в памяти (должен быть объектом того же типа или конвертируемого типа), но его нельзя использовать для изменения его указателя памяти. Вы также можете объявить константный указатель на константный объект. Нельзя использовать ни для изменения указателя, ни переназначить, чтобы он указывал на другой объект. Следующий код иллюстрирует эти тонкости:

Следуйте обычному объявлению соглашения C, объявление следует за использованием, и * в указателе написано на указателе, чтобы указать разыменование. Например, в объявлении int * ptr разыменованная форма * ptr — это int, а ссылочная форма ptr — это указатель на int. Поэтому const меняет имя вправо.

inline

В языках программирования C и C ++ встроенные функции — это функции, которые квалифицируются ключевым словом inline; это служит двум целям. Прежде всего, он действует как инструкция компилятора. Рекомендуется (но не обязательно), чтобы компилятор заменил тело функции, выполнив встроенное расширение, то есть вставив код функции в адрес каждого вызова функции, тем самым сохранив накладные расходы на вызов функции. В этом отношении он похож на спецификатор класса хранения регистров, который аналогично дает подсказку по оптимизации. Вторая цель встраивания — изменить поведение ссылки; это сложная деталь. Это необходимо из-за модели раздельной компиляции + связывания C / C ++, особенно потому, что определение (тело) функции должно повторяться во всех единицах преобразования, где оно используется для включения встраивания во время компиляции. Если функция имеет внешнюю связь, вызывает конфликт в процессе связывания (нарушает уникальность внешних символов).

Встроенные функции могут быть написаны на C или C ++, как показано ниже:

Затем объявите следующее:

Может быть переведено в (если компилятор решает встроить, обычно требуется включить оптимизацию):

Это может увеличить скорость выполнения при реализации алгоритма сортировки, который выполняет большое количество обменов.

Static: Многоцелевое ключевое слово

Большинство ключевых слов C++ позволяют сделать одну вещь. Вы используете int для объявления целочисленной переменной, или тогда, когда функция возвращает целое значение, или принимает целое число в качестве аргумента. Вы используете оператор new для выделения памяти, а оператор delete — для ее освобождения. Вы можете использовать const для указания, что значение переменной не может быть изменено. По иронии судьбы, ключевое слово static , хотя и означает «неизменный», имеет несколько (и, видимо, не связанных между собой) способов использования. Ключевое слово static может быть использовано в трех основных контекстах:

  • внутри функции;
  • внутри определения класса;
  • перед глобальной переменной внутри файла, составляющего многофайловую программу.

Использование static внутри функции является самым простым. Это просто означает, что после того, как переменная была инициализирована, она остается в памяти до конца программы. Вы можете думать об этом, как о переменной, которая хранит свое значение до полного завершения программы. Например, вы можете использовать статическую переменную для записи количества раз, когда функция была вызвана, просто добавив строки static int count = 0; и count++; в функцию. Так как count является статической переменной, строка static int count = 0; будет выполняться только один раз. Всякий раз, когда функция вызывается, count будет иметь последнее значение, данное ему.

Вы также можете использовать static таким образом, чтобы предотвратить переинициализацию переменной внутри цикла. Например, в следующем коде переменная number_of_times будет равна 100, несмотря на то что строка static int number_of_times = 0; находится внутри цикла, где она, по-видимому, должна исполнятся каждый раз, когда программа доходит до цикла. Хитрость заключается в том, что ключевое слово static препятствует повторной инициализации переменной. Одной из особенностей использования ключевого слова static является то, что оно автоматически устанавливает переменную в ноль для вас — но не полагайтесь на это (это делает ваши намерения неясными).

Вы можете использовать статические переменные для сохранения информации о последнем значении возвращенной функции, например, если вы хотите сохранить максимальное значение, рассчитанное по функции. Если вы делаете разбор строки, вы можете также хранить последний знак, возвращенный функцией, для того, чтобы иметь возможность вызвать ее с аргументом, означающим, что она должна вернуть последний знак.

Второе использование static — внутри определения класса. Хотя большинство переменных, объявленных внутри класса могут иметь разное значение в каждом экземпляре класса, статические поля класса будут иметь то же значение для всех экземпляров данного класса и даже не обязательно создавать экземпляр этого класса. Полезно представить себе, что статические переменные класса содержат информацию, необходимую для создания новых объектов (например в фабрике классов). Например, если вы хотите пронумеровать экземпляры класса, можно использовать статическую переменную для отслеживания последнего используемого номера. Важно отметить, что хорошим тоном при использовании статических переменных класса является использование class_name::х; , а не instance_of_class.x; . Это помогает напомнить программисту, что статические переменные не принадлежат к одному экземпляру класса, и что вам не обязательно создавать экземпляр этого класса. Как вы уже, наверное, заметили, для доступа к static можно использовать оператор области видимости, :: , когда вы обращаетесь к нему через имя класса.

Важно иметь в виду, при отладке или реализации программы с использованием static , что вы не можете инициализировать его внутри класса. В самом деле, если вы решите написать весь код класса в файл заголовка, вы даже не сможете инициализировать статическую переменную внутри файла заголовка; сделайте это в файле .cpp . Кроме того, вам необходимо инициализировать статические члены класса, или их не будет в области видимости. (Синтаксис немного странный: type class_name::static_variable = value .)

У вас также могут быть статические функции класса. Статические функции — это функции, которые не требуют экземпляра класса и вызываются так же, по аналогии со статическими переменным, с именем класса, а не с именем объекта. Например, a_class::static_function(); , а не an_instance.function(); . Статические функции могут работать только со статическими членами класса, так как они не относятся к конкретным экземплярам класса. Статические функции могут быть использованы для изменения статических переменных, отслеживать их значения — например, вы можете использовать статическую функцию, если вы решили использовать счетчик, чтобы дать каждому экземпляру класса уникальный идентификатор.

Например, вы можете использовать следующий код:

Обратите внимание, что вы должны включать тип статической переменной, когда вы устанавливаете его!

установит идентификатор на следующий идентификационный номер, не используемый любым другим объектом user . Обратите внимание, что это хороший стиль объявления идентификатора как константы.

Последнее использование static — глобальная переменная в файле кода. В этом случае использование static указывает, что исходный код в других файлах, которые являются частью проекта, не может получить доступ к переменной. Только код внутри того же файла может увидеть переменную (её область видимости ограничена файлом). Эта техника может быть использована для моделирования объектно-ориентированного кода, потому что она ограничивает видимость переменных и таким образом помогает избежать конфликта имен. Этот способ использования static является пережитком Cи.

Классы памяти в Си. Какие классы памяти существуют в языке си?

Обычная переменная, объявленная внутри блока без указания для неё иного класса, имеет класс памяти auto по умолчанию.

Такая переменная видна внутри блока.

Переменные с классом памяти auto размещаются в стеке.

Итак по умолчанию локальная переменная имеет класс памяти auto.

Класс памяти static

Переменные, объявленные как static, т.е. статические, сохраняют своё значение между вызовами. Пример для переменных static:

Получаем: Классы памяти в Си

При втором вызове значение переменной с классом памяти auto, т.е. autoVar в этом примере, потерялось, а значение переменной static, т.е. staticVar в этом примере, сохранилось.

Ещё одна тонкость. static переменная инициализируется только один раз. Если бы это было иначе, то значение статической staticVar обнулилось бы при повторном вызове.

По умолчанию все глобальные переменные являются статическими. И видны они только в своём файле.

Статические функции в Си

Если функция объявлена как static, т.е. статическая, то она видна только в своём файле. Из другого файла к static функции обратиться нельзя. Таким образом, ключевое слово static применительно к функциям в C означает их закрытость для обращений из других файлов программы. Пример. Объявим в отдельном модуле две функции: одну статическую, а другую обычную:

static функция staticFunc будет доступна только в данном модуле. Простая функция simpleFunc доступна для других модулей программы, т.е. её класс памяти extern. Память extern устанавливается для функций по умолчанию. Код основного модуля:

Строка, закрытая комментарием, ошибочна, ведь статическая функция staticFunc объявлена в другом модуле, а значит недоступна в основном модуле.

Класс памяти extern в C

Класс памяти extern в C используем в двух случаях:

  • если переменная объявляется в программе ниже, чем ссылка на неё;
  • если переменная объявлена в другом модуле.

Пример основного модуля:

и другого модуля программы:

Получаем: Класс памяти extern в C

Класс памяти register в C

Применение модификатора register есть рекомендация компилятору хранить данную переменную в регистре, а не в оперативной памяти. Не факт, что так и будет. Если программа часто обращается к переменной, то есть смысл объявить её с модификатором register. Пример:

Применять register можно только к near указателям и целому типу. Использовать register можно и при указании формальных параметров функций. Примеры:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *