Урок №124. статические переменные-члены класса
Содержание:
- Галопом по Европам, или Кратко об интерфейсах
- Статические методы Java
- Еще один пример
- Статическая переменная
- Статические классы
- Константы, поля и структуры для чтения
- Статические переменные
- Программная поддержка
- Музыка
- Оператор dynamic_cast и Ссылки
- Ключевое слово static
- Статические методы
- Классы памяти переменных
- Наследование статических методов
Галопом по Европам, или Кратко об интерфейсах
Set — это неупорядоченное множество уникальных элементов.
Например, мешочек с бочонками для игры в лото: каждый номер от 1 до 90 встречается в нём ровно один раз, и заранее неизвестно, в каком порядке бочонки вынут при игре.
List — упорядоченный список, в котором у каждого элемента есть индекс. Дубликаты значений допускаются.
Например, последовательность букв в слове: буквы могут повторяться, при этом их порядок важен.
Queue — очередь. В таком списке элементы можно добавлять только в хвост, а удалять — только из начала. Так реализуется концепция FIFO (first in, first out) — «первым пришёл — первым ушёл». Вам обязательно напомнят это правило, если попробуете пролезть без очереди в магазине:
А ещё есть LIFO (last in, first out), то есть «последним пришёл — первым ушёл». Пример — стопка рекламных буклетов на ресепшене отеля: первыми забирают самые верхние (положенные последними). Структуру, которая реализует эту концепцию, называют стеком.
Deque может выступать и как очередь, и как стек. Это значит, что элементы можно добавлять как в её начало, так и в конец. То же относится к удалению.
Будет здорово, если на собеседовании вы назовёте Deque правильно: «дэк», а не «дэ́кью», как часто говорят.
Map состоит из пар «ключ-значение». Ключи уникальны, а значения могут повторяться. Порядок элементов не гарантирован. Map позволяет искать объекты (значения) по ключу.
Пример: стопка карточек с иностранными словами и их значениями. Для каждого слова (ключ) на обороте карточки есть вариант перевода (значение), а вытаскивать карточки можно в любом порядке.
Не путайте интерфейс Collection и фреймворк Collections. Map не наследуется от интерфейса Collection, но входит в состав фреймворка Collections.
Статические методы Java
Если ключевое слово static применяется в объявлении метода, то это static method Java.
- Статический метод относится к классу, а не к объекту класса;
- Статический метод можно вызывать без создания экземпляра класса;
- Статический метод может получать доступ к статическому члену и изменять его значение.
Пример статического метода
//Программа изменяет общее свойство всех объектов (статическое поле). class Student9{ int rollno; String name; static String college = "ITS"; static void change(){ college = "BBDIT"; } Student9(int r, String n){ rollno = r; name = n; } void display (){System.out.println(rollno+" "+name+" "+college);} public static void main(String args[]){ Student9.change(); Student9 s1 = new Student9 (111,"Karan"); Student9 s2 = new Student9 (222,"Aryan"); Student9 s3 = new Student9 (333,"Sonoo"); s1.display(); s2.display(); s3.display(); } }
Проверить сейчас
Вывод: 111 Karan BBDIT 222 Aryan BBDIT 333 Sonoo BBDIT
Другой пример Java static метода, выполняющего обычный расчет
//Программа для получения куба заданного числа статическим методом class Calculate{ static int cube(int x){ return x*x*x; } public static void main(String args[]){ int result=Calculate.cube(5); System.out.println(result); } }
Проверить сейчас
Вывод: 125
Ограничения для статического метода
Существует два основных ограничения для статического метода:
- Статический метод не может использовать нестатические члены или напрямую вызывать нестатические методы;
- this и super нельзя использовать в статическом контексте.
class A{ int a=40;//нестатический public static void main(String args[]){ System.out.println(a); } }
Проверить сейчас
Вывод: Compile Time Error (ошибка времени компиляции)
Вопрос: почему основной main метод Java является статическим?
Ответ: поскольку объект не является обязательным для вызова статического метода, если он был нестатическим. Виртуальная машина сначала вызывает Java public static void main(), что приводит к выделению дополнительной памяти.
Еще один пример
Статические методы можно определять вне тела класса. Это работает так же, как и с обычными методами. Например:
#include <iostream>
class IDGenerator
{
private:
static int s_nextID; // объявление статической переменной-члена
public:
static int getNextID(); // объявление статического метода
};
// Определение статической переменной-члена находится вне тела класса
Обратите внимание, мы не используем здесь ключевое слово static.
// Начинаем генерировать ID с 1
int IDGenerator::s_nextID = 1;
// Определение статического метода находится вне тела класса. Обратите внимание, мы не используем здесь ключевое слово static
int IDGenerator::getNextID() { return s_nextID++; }
int main()
{
for (int count=0; count
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include
classIDGenerator
{
private
staticints_nextID;// объявление статической переменной-члена
public
staticintgetNextID();// объявление статического метода
};
// Определение статической переменной-члена находится вне тела класса
Обратите внимание, мы не используем здесь ключевое слово static.
// Начинаем генерировать ID с 1
intIDGenerator::s_nextID=1;
// Определение статического метода находится вне тела класса. Обратите внимание, мы не используем здесь ключевое слово static
intIDGenerator::getNextID(){returns_nextID++;}
intmain()
{
for(intcount=;count
Результат выполнения программы:
Обратите внимание, поскольку все переменные и функции этого класса являются статическими, то нам не нужно создавать объект этого класса для работы с ним! Статическая переменная-член используется для хранения значения следующего идентификатора, который должен быть ей присвоен, а статический метод — для возврата идентификатора и его увеличения
Статическая переменная
Когда вы объявляете переменную как статическую, создается единственная копия переменной, которая распределяется между всеми объектами на уровне класса. Это, по сути, глобальные переменные, все экземпляры класса имеют одну и ту же статическую переменную. Могут быть созданы только на уровне класса.
Теперь давайте разберемся с этим на примере.
// Java program demonstrate execution of static blocks and variables import java.util.*; public class VariableExample { // static variable static int j = n(); // static block static { System.out.println("Inside the static block"); } // static method static int n() { System.out.println("from n "); return 20; } // static method(main !!) public static void main(String[] args) { System.out.println("Value of j : "+j); System.out.println("Inside main method"); } }
Когда вы выполняете вышеуказанную программу, она выполняет статический блок и переменную в порядке, как определено в вышеуказанной программе.
Вывод:
from n Inside the static block Value of j: 20 Inside main method
Статические классы
Статические вложенные классы можно использовать вне своего родительского класса. Если у такого класса стоит модификатор доступа public, его можно использовать в любом месте программы. Такие классы фактически ничем не отличаются от любого обычного класса. Хотя есть пара отличий.
Имя класса
Если вы хотите обратиться к статическому вложенному классу не из его класса-родителя, а из другого места в программе, вам нужно будет указать имя класса: оно состоит из имени класса родителя и имени вложенного класса. Общий вид этого имени такой:
Примеры:
Класс родитель | Вложенный класс | Полное имя вложенного класса |
---|---|---|
Если вложенный класс имеет свой вложенный класс, их имена просто склеиваются через точку.
Типичный пример вложенного класса в JDK — это класс Entry внутри класса Map. Если вы хотите получить множество пар элементов из объекта , то метод вернет вам множество пар типа .
Вот запись — это и есть внешний и внутренний классы.
Создание объекта
Создать объект вложенного статического класса очень легко. Выглядит это так:
Все, как и с обычными классами, только имя двойное.
Обращение к статическим методам
Если у статического класса есть статические методы, обращаться к ним можно точно так же, как к статическим методам обычных классов (только имя класса теперь двойное).
Обращение к статическим переменным
Обращаться к публичным статическим переменным вложенного класса тоже легко:
Константы, поля и структуры для чтения
Последнее обновление: 03.10.2019
Полями класса называются обычные переменные уровня класса. Мы уже ранее рассматривали переменные — их объявление и инициализацию. Однако
некоторые моменты мы еще не затрагивали, например, константы и поля для чтения.
Константы
Константы характеризуются следующими признаками:
-
Константа должна быть проинициализирована при определении
-
После определения значение константы не может быть изменено
Константы предназначены для описания таких значений, которые не должны изменяться в программе. Для определения констант используется
ключевое слово const:
const double PI = 3.14; const double E = 2.71;
При использовании констант надо помнить, что объявить мы их можем только один раз и что к моменту компиляции они должны быть определены.
class MathLib { public const double PI=3.141; public const double E = 2.81; public const double K; // Ошибка, константа не инициализирована } class Program { static void Main(string[] args) { MathLib.E=3.8; // Ошибка, значение константы нельзя изменить } }
Также обратите внимание на синтаксис обращения к константе. Так как неявно это статическое поле, для обращения к ней необходимо использовать
имя класса
class MathLib { public const double PI=3.141; } class Program { static void Main(string[] args) { Console.WriteLine(MathLib.PI); } }
Но следует учитывать, что мы не можем объявить константу с модификатором static. Но в этом собственно и нет смысла.
Константу можно определить как на уровне класса, так и внутри метода:
class MathLib { public double GetCircleArea(double radius) { const double PI = 3.141; return PI * radius * radius; } }
Поля для чтения
Поля для чтения можно инициализировать при их объявлении либо на уровне класса, либо инициилизировать и изменять в конструкторе. Инициализировать или изменять их значение в других местах нельзя, можно только считывать их значение.
Поле для чтения объявляется с ключевым словом readonly:
class MathLib { public readonly double K = 23; // можно так инициализировать public MathLib(double _k) { K = _k; // поле для чтения может быть инициализировано или изменено в конструкторе после компиляции } public void ChangeField() { // так нельзя //K = 34; } } class Program { static void Main(string[] args) { MathLib mathLib = new MathLib(3.8); Console.WriteLine(mathLib.K); // 3.8 //mathLib.K = 7.6; // поле для чтения нельзя установить вне своего класса Console.ReadLine(); } }
Сравнение констант
-
Константы должны быть определены во время компиляции, а поля для чтения могут быть определены во время выполнения программы.
Соответственно инициализировать константу можно устанновить только при ее определении.
Поле для чтения можно инициализировать либо при его определении, либо в конструкторе класса.
-
Константы не могут использовать модификатор static, так как уже неявно являются статическими. Поля для чтения могут быть как статическими, так и не статическими.
Структуры для чтения
Кроме полей для чтения в C# можно определять структуры для чтения. Для этого они предваряются модификатором readonly:
readonly struct User { }
Особенностью таких структур является то, что все их поля должны быть также полями для чтения:
readonly struct User { public readonly string name; public User(string name) { this.name = name; } }
То же самое касается и свойств, которые должны быть доступны только для чтения:
readonly struct User { public readonly string Name { get; } // указывать readonly необязательно public int Age { get; } // свойство только для чтения public User(string name, int age) { this.Name = name; this.Age = age; } }
НазадВперед
Статические переменные
Когда класс загружается в память, для него сразу создается статический объект класса. Этот объект хранит статические переменные класса (статические поля класса). Статический объект класса существует, даже если не был создан ни один обычный объект класса.
Когда мы описываем переменные в классе, мы указываем, будут ли эти переменные созданы всего один раз или же нужно создавать их копии для каждого объекта. По умолчанию создаётся новая копия переменной для каждого объекта.
Статическая (static) же переменная привязана к статическому объекту класса и всегда существует в единственном экземпляре.
Чтобы создать статическую переменную класса, нужно перед ее именем написать ключевое слово . Общий вид объявления статической переменной такой:
Если статической переменной не присвоить стартовое значение, она инициализируется значением по умолчанию:
Тип | Значение по умолчанию |
---|---|
(то же самое, что и ) | |
и любые классы |
Примеры:
Код | Примечание |
---|---|
Обращаться к статической переменной в ее классе можно просто по имени. Если обращение идет из другого класса, то перед именем статической переменной нужно писать имя класса.
Пример:
Переменная | Класс | Обращение к переменной вне класса |
---|---|---|
Переменная , вне класса не видна | ||
Переменная , вне класса не видна | ||
Переменная , вне класса не видна |
Программная поддержка
В основном рассматривается поддержка чисел с плавающей точкой в языках C/C++.
Можно упомянуть, что в 2006 г. Уильям Кэхен отмечал, что
Currently the only fully conforming standard programming language is C99, and only on a very few machines…
Для других языков читатель может обратиться к документации по ним (и их реализациям), описывающей поддержку чисел с плавающей точкой (в предположении, что таковая существует).
Стандарты C и C++
Стандарты C и C++ не требуют, чтобы арифметика с плавающей точкой реализовала именно IEEE754.
Для соответствующей IEEE754 требования изложены в Appendix F Стандарта C (в C++ соответствующий раздел отсутствует).
В C для проверки поддержки IEEE754 предназначено макро
__STDC_IEC_559__
определённое только если арифметика с плавающей точкой реализует IEEE754.
В C++ для типа type значение константы
std::numeric_limits<type>::is_iec559
определяет, соответствует ли данный тип IEEE754.
На практике однако данные определения проблематичны.
Компиляторы могут не определять __STDC_IEC_559__, или определять без полного соответствия стандарту.
Соответствие между C и IEEE754
Appendix F стандарта C подробно излагает соответствие между операциями IEEE754 и конструкциями (и функциями) языка C.
Подробнее см. в разделе F.3.
Компиляторы
В основном рассматриваются компиляторы GCC и Visual Studio; в меньшей степени — Clang и ICC.
Стоит отметить, что т. к. компилятор Visual Studio не поддерживает тип double-extended (см. ниже), то среда по умолчанию выставляет точность FPU в double, таким образом регистры FPU имеют точность double но диапазон double-extended. Также DirectX имеет тенденцию выставлять точность в float.
Типы
Яыки C/C++ предоставляют (согласно стандарту я) 3 типа с плавающей точкой: float, double и long double.
Типы float и double, практически всюду соответствуют binary32 и binary64 стандарта IEEE-754.
long double менее однозначен. Для x86/x86_64 в GCC и Clang он по-умолчанию соответствует 80-битному расширенному формату Intel x87. Для Visual Studio и ICC — он физически соответствует тому же типу, что и double (хотя логически (с т. з. Стандарта) является самостоятельным типом, в частности по double и long double возможна перегрузка функций). Для ICC на Windows можно воспользоваться флагом /Qlong-double, который меняет long double на 80-битный x87 (тип занимает 16 байт из соображений выравнивания). Для Visual Studio подобная возможность отсутствует — поддержка 80-битного расширенного типа x87 в ней отсутствует начиная с 32-битных версий (она присутствовала в 16-битных).
Размер типа long double (в смысле значения sizeof(long double)) для 80-битного типа x87 типично не равен 10 байтам, из соображений выравнивания: реальные значения обычно равны 12 или 16 байт. Для GCC значение можно изменить флагами компиляции -malign-double, -mno-align-double, -m96bit-long-double, -m128bit-long-double. Не используемые байты могут иметь произвольные значения.
На некоторых платформах long double означает binary128, либо использует пару 64-битных вещественных чисел, для достижения 106-битной точности, но с диапазоном обычного double (этот подход называется double-double, подробнее см. в разделе ).
На многих платформах (в частности x86/x86_64) GCC по умолчанию поддерживает (предоставляет в качестве расширения) нестандартный тип __float128 (а также __float80). Реализация этого типа (на архитектурах не поддерживающих binary128) программная и, соответственно, медленная. Суффикс ‘q’/’Q’ для литералов этого типа поддерживается для C, но не для C++. Пример:
+ Показать
Музыка
Влияние
Как говорят участники группы Static-X они «выросли на Kiss». Так
же большое влияние на участников группы оказало творчество таких групп как: Ministry, Slayer, Pantera, Metallica, White Zombie, Nine Inch Nails, Anthrax, Fear Factory, Danzig, Coal Chamber, Prong, Type O Negative, Korn.
Музыкальный стиль
Хотя группа описывает свой музыкальный стиль как «evil disco» и «rhytmic trancecore», звучание Static-X имеет много общего с индастриал-металом и ню-металом, группой используется перегруженное звучание, электронная клавиатура, а также группа часто в своих песнях загоняет басовые рифы и так же часто используется быстро изменяющийся, агрессивный вокал. В своих песнях группа часто применяет использование синтезаторов и вставки образцов и отрывки диалогов из кино. Например, такие звуковые эффекты как гул космического корабля в песне «A Dios Alma Perdida» и иностранный разговор из фильма Лазерный взрыв. Новый элемент, используемый на их альбоме Cannibal — присутствие соло гитары, как и в предыдущих альбомах, за исключением на альбомах Shadow Zone в песне «Destroy All» и «Start a War» и в песне «Pieces». Static-X были упомянуты разнообразной прессой, как самая индастриал-метал и ню-метал-группа, а музыканты, обсуждая это, заявляли, что они — большая индастриал-метал-группа, чем какая либо ещё.
Static-X считают своей задачей делать музыку, которая включает в себя электронные эффекты техно, агрессивность альтернативного рока, готические обертоны и элементы индастриала и ню-метала одновременно и мешать всё это во что-то уникальное.
«Для нас всегда было важно суметь повторить наше студийное звучание на концерте перед публикой» — объясняет Уэйн Статик. «Нет ничего хуже, чем пойти на концерт группы, которая не может создать ощущение того, что шоу — это продолжение записанной музыки» — добавляет Кен Джей.
«Наши концерты походят на минирэйв с неумолимой и интенсивной отдачей, которая затрагивает чувства всей аудитории и никого не оставляет равнодушной».. По словам Уэйна Статика:
По словам Уэйна Статика:
Работа с другими группами
По словам участников группы, во многом группа стала известной благодаря
совместным выступлениям Static-X с такими известными группами, как
System Of A Down, Slayer, Fear Factory, Linkin Park и другими.
Otsego песни
Песни с приставкой Otsego присутствуют на первых четырёх альбомах. Песни называются: «Otsegolation», «Otsego Undead», «Otsegolectric» и «Otsego Amigo».
Альбом Cannibal первый, где нет песни с таким названием. Объяснение названия Otsego давалось в журнале Metal Edge Magazine в 2003 году, где Уэйн Статик сказал:
Static-X Guitar Anthology, Chaos Comics и Игра Cannibal
«Static-X Guitar Anthology» это книга гитарных табов выпущенная группой Static-X 1 октября 2004 года. Содержание книги включает 16 песен и гитарных рифов с альбомов Wisconsin Death Trip, Machine, и Shadow Zone. Книга включает гитарные табы таких песен как: • «Black And White» • «Bled For Days» • «Cold» • «Destroy All» • «Get To The Gone» • «I’m With Stupid» • «Love Dump» • «Monster» • «New Pain» • «Permanence» • «Push It» • «Shadow Zone» • «So» • «The Only» • «The Trance Is The Motion» • «This Is Not».
Так же Static-X издали собственный комикс — «Chaos Comics», где главными персонажами являются сами участники группы. Это издание шло в комплекте с бонус-CD к альбому Machine, где находились живой аудиотрек «This Is Not» и клип на эту же песню.
Для раскрутки своего альбома Cannibal группа выпустила одноимённую флеш-игру, по сюжету которой вам предстоит успеть пройти через толпу фанатов на сцену за определённое время, пока не начался концерт.
Оператор dynamic_cast и Ссылки
Хотя во всех примерах, приведенных выше, мы использовали динамическое приведение с указателями (что является наиболее распространенным), оператор dynamic_cast также может использоваться и со ссылками. Работа dynamic_cast со ссылками аналогична работе с указателями:
#include <iostream>
#include <string>
class Parent
{
protected:
int m_value;
public:
Parent(int value)
: m_value(value)
{
}
virtual ~Parent() {}
};
class Child: public Parent
{
protected:
std::string m_name;
public:
Child(int value, std::string name)
: Parent(value), m_name(name)
{
}
const std::string& getName() { return m_name; }
};
int main()
{
Child banana(1, «Banana»);
Parent &p = banana;
Child &ch = dynamic_cast<Child&>(p); // используем оператор dynamic_cast для конвертации ссылки класса Parent в ссылку класса Child
std::cout << «The name of the Child is: » << ch.getName() << ‘\n’;
return 0;
}
1 |
#include <iostream> classParent { protected intm_value; public Parent(intvalue) m_value(value) { } virtual~Parent(){} }; classChildpublicParent { protected std::stringm_name; public Child(intvalue,std::stringname) Parent(value),m_name(name) { } conststd::string&getName(){returnm_name;} }; intmain() { Childbanana(1,»Banana»); Parent&p =banana; Child&ch =dynamic_cast<Child&>(p);// используем оператор dynamic_cast для конвертации ссылки класса Parent в ссылку класса Child std::cout<<«The name of the Child is: «<<ch.getName()<<‘\n’; return; } |
Поскольку в языке C++ не существует «нулевой ссылки», то dynamic_cast не может возвратить «нулевую ссылку» при сбое. Вместо этого, dynamic_cast генерирует исключение типа std::bad_cast (мы поговорим об исключениях чуть позже).
Ключевое слово static
Ключевое слово static может использоваться в различных контекстах, однако сегодня речь пойдет о его использовании именно в классах C#. В русской литературе по программированию на языке C# можно встретить такие определения, как «статичный», «статический» и так далее. В любом случае, это означает, что какой-либо член класса (свойство, метод) определен с атрибутом .
В документации Microsoft по языку C# дословно сказано следующее:
Модификатор static используется для объявления статического члена, принадлежащего собственно типу, а не конкретному объекту.Microsoft
Посмотрим, что это означает на практике.
Статические методы
Статические методы декларируются при помощи декоратора . Им не нужен определённый первый аргумент (ни , ни ).
Их можно воспринимать как методы, которые “не знают, к какому классу относятся”.
Таким образом, статические методы прикреплены к классу лишь для удобства и не могут менять состояние ни класса, ни его экземпляра.
С теорией достаточно. Давайте разберёмся с работой методов, создав объект нашего класса и вызвав поочерёдно каждый из методов: instancemethod, classmethod and staticmethod.
Пример выше подтверждает то, что метод instancemethod имеет доступ к объекту класса ToyClass через аргумент . Кстати, вызов функции используется лишь для удобства, то есть можно использовать и .
Теперь давайте вызовем метод класса:
Мы видим, что метод класса classmethod() имеет доступ к самому классу ToyClass, но не к его конкретному экземпляру объекта. Запомните, в Python всё является объектом. Класс тоже объект, который мы можем передать функции в качестве аргумента.
Заметьте, что и — не обязательные названия и эти параметры можно называть иначе.
Это лишь общепринятые обозначения, которым следуют все. Тем не менее они должны находиться первыми в списке параметров.
Вызовем статический метод:
Да, это может вас удивить, но статические методы можно вызывать через объект класса. Вызов через точку нужен лишь для удобства. На самом же деле в случае статического метода никакие аргументы ( или) методу не передаются.
То есть статические методы не могут получить доступ к параметрам класса или объекта. Они работают только с теми данными, которые им передаются в качестве аргументов.
Теперь давайте вызовем те же самые методы, но на самом классе.
Метод класса и статический метод работают, как нужно. Однако вызов метода экземпляра класса выдаёт TypeError, так как метод не может получить на вход экземпляр класса.
Теперь, когда вы знаете разницу между тремя видами методов, давайте рассмотрим реальный пример для понимания того, когда и какой метод стоит использовать. Пример взят отсюда.
Классы памяти переменных
По умолчанию, локальные переменные имеют класс auto. Такие переменные располагаются на стеке а их область видимости ограничена своим блоком. Запись
#include <conio.h> #include <stdio.h> void main() { int x = 10; { int x = 20; { int x = 30; printf(«%d\n», x); } printf(«%d\n», x); } printf(«%d\n», x); getch(); }
идентична
#include <conio.h> #include <stdio.h> void main() { int auto x = 10; { int auto x = 20; { int auto x = 30; printf(«%d\n», x); } printf(«%d\n», x); } printf(«%d\n», x); getch(); }
Очевидно, что глобальные переменные не могут быть объявлены как auto, потому что располагаются в data-сегменте.
Следующий класс памяти – register. Когда мы определяем регистровую переменную, то мы просим компилятор, чтобы переменная располагалась в регистре, а не в оперативной памяти. Компилятор может сделать переменную регистровой, если позволяют условия (регистры не заняты, и по мнению компилятора это не приведёт к увеличению издержек). Регистровые переменные определяются с помощью служебного слово register перед типом
register int x = 20; register int y = 30;
Так как регистровая переменная не имеет адреса, то к ней не применима операция взятия адреса, это вызовет ошибку во время компиляции. Аргументы функции также могут быть заданы как register. Внутри функции они будут вести себя также, как и регистровые переменные.
Следующий класс памяти – статический. Переменные, объявленные как static, хранятся в data или в bss сегменте. Отличительной чертой является то, что время их жизни совпадает с временем жизни приложения, как и у глобальных переменных. Но в отличие от глобальных переменных, область видимости ограничена только блоком, в котором они определены.
#include <conio.h> #include <stdio.h> unsigned long long factorial(unsigned char n) { static unsigned char prevArg = 0; static long long prevAns = 1; if (n == prevArg) { printf(«return previous answer\n»); return prevAns; } else { unsigned i = 0; printf(«count new answer\n»); prevAns = 1; for (i = 1; i <= n; i++) { prevAns *= i; } prevArg = n; return prevAns; } } void main() { printf(«!%d == %llu\n», 10, factorial(10)); printf(«!%d == %llu\n», 10, factorial(10)); printf(«!%d == %llu\n», 11, factorial(11)); printf(«!%d == %llu\n», 11, factorial(11)); getch(); }
В этом примере переменные prevArg и prevAns инициализируются единожды, и не уничтожаются после выхода из функции. Переменная prevArg используется для хранения предыдущего аргумента функции, а prevAns для хранения предыдущего результата. Если аргумента функции совпадает с предыдущим, то возвращается ранее вычисленное значение, иначе оно вычисляется по-новому.
Другой показательный пример – функция-генератор, которая при каждом вызове возвращает новое значение.
#include <conio.h> #include <stdio.h> int next() { static int counter = 0; counter++; return counter; } void main() { printf(«%d\n», next()); printf(«%d\n», next()); printf(«%d\n», next()); printf(«%d\n», next()); printf(«%d\n», next()); _getch(); }
Если бы служебное слово static отсутствовало, то каждый раз при вызове функции локальная переменная counter снова создавалась, инициализировалась и уничтожалась после выхода из функции.
Статическая переменная может иметь только константную инициализацию. Например, она не может быть инициализирована вызовом функции.
… static double x = foo(3); //Ошибка …
Переменная, объявленная как static, должна иметь только один экземпляр в данной области видимости и вне этой области видимости не видна. Глобальная переменная, объявленная как static, видна только в своём файле.
Напротив, переменная, объявленная как extern может быть использована в других файлах при условии, что она была определена.
Наследование статических методов
Но как будут
вести себя статические методы при наследовании классов? Предположим, что мы
хотим добавить еще одного специализированного пользователя Admin:
class Admin extends Users { constructor(name, old, login, psw) { super(name, old); this.login = login; this.psw = psw; } }
Мы расширяем
базовый класс Users и добавляем еще
два свойства: login и psw. Будет ли
статический метод compareOld доступен в дочернем классе Admin? Да, будет и,
далее, мы можем создать такого пользователя:
let u2 = new Admin("Федор", 19, "aaa", "0123");
и сравнить их,
вызывая статический метод через класс Admin:
console.log( Admin.compareOld(u1, u2) );
То есть, метод compareOld можно вызывать
и через класс Users и через класс Admin. Разницы
никакой не будет. Это происходит по той причине, что свойство __proto__ класса Admin ссылается на
класс Users:
Если же добавить
еще один статический метод, но уже в класс Admin:
static createAdmin(name, old) { return new this(name, old, "admin", "root"); }
то картина будет
такой:
В методе createAdmin мы создаем
нового пользователя Admin с использованием только двух
параметров: name, old. Остальные два
задаются по умолчанию как: «admin», «root». Причем,
ключевое слово this здесь будет ссылаться на класс, указанный перед
точкой, при вызове данного метода. Например:
let u3 = Admin.createAdmin("Сергей", 33);
Здесь this ссылается на Admin, поэтому будет
создан новый объект класса Admin. А вот если мы в методе compareOld
добавим вывод:
console.log(this == Admin);
и вызовем его
двумя способами:
Users.compareOld(u1, u2); // false Admin.compareOld(u1, u2); // true
то в первом
случае this будет ссылаться
на Users, а во втором –
на Admin.