Телевизоры. Приставки. Проекторы и аксессуары. Технологии. Цифровое ТВ

Что означает static в c. Статические методы в Java(static methods). Совместное использование элемента данных

То мы можем получить к ним доступ напрямую через имя класса и оператор разрешения области видимости. Но что, если статические переменные-члены являются закрытыми? Рассмотрим следующий код:

В этом случае мы не можем напрямую получить доступ к Anything::s_value из main(), так как этот член является private. Обычно, доступ к закрытым членам класса осуществляется через методы public. Хотя мы могли бы создать обычный метод для получения доступа к s_value , но нам тогда бы пришлось создавать объект этого класса для использования метода! Есть вариант получше: мы можем сделать метод статическим.

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

class Anything { private: static int s_value; public: static int getValue() { return s_value; } // статический метод }; int Anything::s_value = 3; // определение статической переменной-члена класса int main() { std::cout << Anything::getValue() << "\n"; }

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

Статические методы не имеют указателя *this

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

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

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

Статические методы можно определять вне тела класса. Это работает так же, как и с обычными методами. Например:

#include 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 < 4; ++count) std::cout << "The next ID is: " << IDGenerator::getNextID() << "\n"; return 0; }

#include

class IDGenerator

private :

static int s_nextID ; // объявление статической переменной-члена

public :

static int getNextID () ; // объявление статического метода

// Начинаем генерировать ID с 1

int IDGenerator :: s_nextID = 1 ;

int IDGenerator :: getNextID () { return s_nextID ++ ; }

int main ()

for (int count = 0 ; count < 4 ; ++ count )

std :: cout << "The next ID is: " << IDGenerator :: getNextID () << "\n" ;

return 0 ;

Результат выполнения программы выше:

The next ID is: 1
The next ID is: 2
The next ID is: 3
The next ID is: 4

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

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

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

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

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

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

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

class Something { public: static std::vector s_mychars; }; std::vector Something::s_mychars = { "o", "a", "u", "i", "e" }; // определяем статическую переменную-член

class Something

public :

static std :: vector < char > s_mychars ;

std :: vector < char > Something :: s_mychars = { "o" , "a" , "u" , "i" , "e" } ; // определяем статическую переменную-член

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

#include #include class Something { private: static std::vector s_mychars; public: class _nested // определяем вложенный класс с именем _nested { public: _nested() // конструктор _nested инициализирует нашу статическую переменную-член { s_mychars.push_back("o"); s_mychars.push_back("a"); s_mychars.push_back("u"); s_mychars.push_back("i"); s_mychars.push_back("e"); } }; // Статический метод для вывода s_mychars static void getSomething() { for (auto const &element: s_mychars) std::cout << element << " "; } private: static _nested s_initializer; // используем статический объект класса _nested для гарантии того, что конструктор _nested выполнится }; std::vector Something::s_mychars; // определяем нашу статическую переменную-член Something::_nested Something::s_initializer; // определяем наш статический s_initializer, который вызовет конструктор _nested для инициализации s_mychars int main() { Something::getSomething(); return 0; }

#include

#include

class Something

private :

static std :: vector < char > s_mychars ;

public :

class _nested // определяем вложенный класс с именем _nested

public :

Nested () // конструктор _nested инициализирует нашу статическую переменную-член

s_mychars . push_back ("o" ) ;

s_mychars . push_back ("a" ) ;

s_mychars . push_back ("u" ) ;

s_mychars . push_back ("i" ) ;

s_mychars . push_back ("e" ) ;

Большинство ключевых слов 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 является то, что оно автоматически устанавливает переменную в ноль для вас — но не полагайтесь на это (это делает ваши намерения неясными).

For(int ix=0; ix < 10; ix++) { for(int iy = 0; iy < 10; iy++) { static int number_of_times = 0; number_of_times++; } }

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

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

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

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

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

Class user { private: int id; static int next_id; public: static int next_user_id() { next_id++; return next_id; } // остальные методы для класса user user() // конструктор класса { id = user::next_id++; // или вызов метода, id = user.next_user_id(); } }; int user::next_id = 0;

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

User a_user;

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

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

Последнее обновление: 25.12.2018

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

Class Account { public static decimal bonus = 100; public decimal totalSum; public Account(decimal sum) { totalSum = sum + bonus; } } class Program { static void Main(string args) { Console.WriteLine(Account.bonus); // 100 Account.bonus += 200; Account account1 = new Account(150); Console.WriteLine(account1.totalSum); // 450 Account account2 = new Account(1000); Console.WriteLine(account2.totalSum); // 1300 Console.ReadKey(); } }

В данном случае класс Account имеет два поля: bonus и totalSum. Поле bonus является статическим, поэтому оно хранит состояние класса в целом, а не отдельного объекта. И поэтому мы можем обращаться к этому полю по имени класса:

Console.WriteLine(Account.bonus); Account.bonus += 200;

На уровне памяти для статических полей будет создаваться участок в памяти, который будет общим для всех объектов класса.

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

Статические свойства и методы

Подобным образом мы можем создавать и использовать статические методы и свойства:

Class Account { public Account(decimal sum, decimal rate) { if (sum < MinSum) throw new Exception("Недопустимая сумма!"); Sum = sum; Rate = rate; } private static decimal minSum = 100; // минимальная допустимая сумма для всех счетов public static decimal MinSum { get { return minSum; } set { if(value>0) minSum = value; } } public decimal Sum { get; private set; } // сумма на счете public decimal Rate { get; private set; } // процентная ставка // подсчет суммы на счете через определенный период по определенной ставке public static decimal GetSum(decimal sum, decimal rate, int period) { decimal result = sum; for (int i = 1; i <= period; i++) result = result + result * rate / 100; return result; } }

Переменная minSum, свойство MinSum, а также метод GetSum здесь определены с ключевым словом static , то есть они являются статическими.

Переменная minSum и свойство MinSum представляют минимальную сумму, которая допустима для создания счета. Этот показатель не относится к какому-то конкретному счету, а относится ко всем счетам в целом. Если мы изменим этот показатель для одного счета, то он также должен измениться и для другого счета. То есть в отличии от свойств Sum и Rate, которые хранят состояние объекта, переменная minSum хранит состояние для всех объектов данного класса.

То же самое с методом GetSum - он вычисляет сумму на счете через определенный период по определенной процентной ставке для определенной начальной суммы. Вызов и результат этого метода не зависит от конкретного объекта или его состояния.

Таким образом, переменные и свойства, которые хранят состояние, общее для всех объектов класса, следует определять как статические. И также методы, которые определяют общее для всех объектов поведение, также следует объявлять как статические.

Статические члены класса являются общими для всех объектов этого класса, поэтому к ним надо обращаться по имени класса:

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

Нередко статические поля применяются для хранения счетчиков. Например, пусть у нас есть класс User, и мы хотим иметь счетчик, который позволял бы узнать, сколько объектов User создано:

Class User { private static int counter = 0; public User() { counter++; } public static void DisplayCounter() { Console.WriteLine($"Создано {counter} объектов User"); } } class Program { static void Main(string args) { User user1 = new User(); User user2 = new User(); User user3 = new User(); User user4 = new User(); User user5 = new User(); User.DisplayCounter(); // 5 Console.ReadKey(); } }

Статический конструктор

Кроме обычных конструкторов у класса также могут быть статические конструкторы. Статические конструкторы имеют следующие отличительные черты:

    Статические конструкторы не должны иметь модификатор доступа и не принимают параметров

    Как и в статических методах, в статических конструкторах нельзя использовать ключевое слово this для ссылки на текущий объект класса и можно обращаться только к статическим членам класса

    Статические конструкторы нельзя вызвать в программе вручную. Они выполняются автоматически при самом первом создании объекта данного класса или при первом обращении к его статическим членам (если таковые имеются)

Статические конструкторы обычно используются для инициализации статических данных, либо же выполняют действия, которые требуется выполнить только один раз

Определим статический конструктор:

Class User { static User() { Console.WriteLine("Создан первый пользователь"); } } class Program { static void Main(string args) { User user1 = new User(); // здесь сработает статический конструктор User user2 = new User(); Console.Read(); } }

Статические классы

Статические классы объявляются с модификатором static и могут содержать только статические поля, свойства и методы. Например, если бы класс Account имел бы только статические переменные, свойства и методы, то его можно было бы объявить как статический:

Static class Account { private static decimal minSum = 100; // минимальная допустимая сумма для всех счетов public static decimal MinSum { get { return minSum; } set { if(value>0) minSum = value; } } // подсчет суммы на счете через определенный период по определенной ставке public static decimal GetSum(decimal sum, decimal rate, int period) { decimal result = sum; for (int i = 1; i <= period; i++) result = result + result * rate / 100; return result; } }

В C# показательным примером статического класса является класс Math, который применяется для различных математических операций.

Урок 25. Статические функции и элементы данных

До настоящего момента каждый создаваемый вами объект имел свой собственный набор элементов данных. В зависимости от назначения вашего приложения могут быть ситуации, когда объекты одного и того же класса должны совместно использовать один или несколько элементов данных. Например, предположим, что вы пишете программу платежей, которая отслеживает рабочее время для 1000 служащих. Для определения налоговой ставки программа должна знать условия, в которых работает каждый служащий. Пусть для этого используется переменная класса state_of_work. Однако, если все служащие работают в одинаковых условиях, ваша программа могла бы совместно использовать этот элемент данных для всех объектов типа employee. Таким образом, ваша программа уменьшает необходимое количество памяти, выбрасывая 999 копий одинаковой информации. Для совместного использования элемента класса вы должны объявить этот элемент как static (статический). Этот урок рассматривает шаги, которые вы должны выполнить для совместного использования элемента класса несколькими объектами. К концу этого урока вы освоите следующие основные концепции:

    C++ позволяет иметь объекты одного и того же типа, которые совместно используют один или несколько элементов класса.

    Если ваша программа присваивает значение совместно используемому элементу, то все объекты этого класса сразу же получают доступ к этому новому значению.

    Для создания совместно используемого элемента данных класса вы должны предварять имя элемента класса ключевым словом static.

    После того как программа объявила элемент класса как static, она должна объявить глобальную переменную (вне определения класса), которая соответствует этому совместно используемому элементу класса.

    Ваши программы могут использовать ключевое слово static, чтобы сделать метод класса вызываемым в то время, как программа, возможно, еще не объявила каких-либо объектов данного класса.

СОВМЕСТНОЕ ИСПОЛЬЗОВАНИЕ ЭЛЕМЕНТА ДАННЫХ

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

private: static int shared_value;

После объявления класса вы должны объявить элемент как глобальную переменную вне класса, как показано ниже:

int class_name::shared_value;

Следующая программа SHARE_IT.CPP определяет класс book_series, совместно использующий элемент page_count, который является одинаковым для всех объектов (книг) класса (серии). Если программа изменяет значение этого элемента, изменение сразу же проявляется во всех объектах класса:

#include

#include

class book_series

{ public: book_series(char *, char *, float); void show_book(void); void set_pages(int) ; private: static int page_count; char title; char author[ 64 ]; float price; };

int book_series::page__count;

void book_series::set_pages(int pages)

{ page_count = pages; }

book_series::book_series(char *title, char *author, float price)

{ strcpy(book_series::title, title); strcpy(book_series::author, author); book_series::price = price; }

void book_series:: show_book (void)

{ cout << "Заголовок: " << title << endl; cout << "Автор: " << author << endl; cout << "Цена: " << price << endl; cout << "Страницы: " << page_count << endl; }

{ book_series programming("Учимся программировать на C++", "Jamsa", 22.95); book_series word("Учимся работать с Word для Windows", "Wyatt", 19.95); word.set_pages(256); programming.show_book (); word.show_book() ; cout << endl << "Изменение page_count " << endl; programming.set_pages(512); programming.show_book(); word.show_book(); }

Как видите, класс объявляет page_count как static int. Сразу же за определением класса программа объявляет элемент page_count как глобальную переменную. Когда программа изменяет элемент page_count, изменение сразу же проявляется во всех объектах класса book_series.

Совместное использование элементов класса

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

Использование элементов с атрибутами public static, если объекты не существуют

Как вы только что узнали, при объявлении элемента класса как static этот элемент совместно используется всеми объектами данного класса. Однако возможны ситуации, когда программа еще не создала объект, но ей необходимо использовать элемент. Для использования элемента ваша программа должна объявить его как public и static. Например, следующая программа USЕ_MBR.CPP использует элемент page_count из класса book_series, даже если объекты этого класса не существуют:

#include

#include

class book_series

{ public: static int page_count; private: char title ; char author; float price; };

int book_series::page_count;

void main(void) { book_series::page_count = 256; cout << "Текущее значение page_count равно " << book_series::page_count << endl; }

В данном случае, поскольку класс определяет элемент класса page_count как public, программа может обратиться к этому элементу класса, даже если объекты класса book_series не существуют.

ИСПОЛЬЗОВАНИЕ СТАТИЧЕСКИХ ФУНКЦИЙ-ЭЛЕМЕНТОВ

Предыдущая программа иллюстрировала использование статических элементов данных. Подобным образом C++ позволяет вам определить статические функции-элементы (методы). Если вы создаете статический метод, ваша программа может вызывать такой метод, даже если объекты не были созданы. Например, если класс содержит метод, который может быть использован для данных вне класса, вы могли бы сделать этот метод статическим. Ниже приведен класс menu, который использует esc-последовательность драйвера ANSI для очистки экрана дисплея. Если в вашей системе установлен драйвер ANSI.SYS, вы можете использовать метод clear_screen для очистки экрана. Поскольку этот метод объявлен как статический, программа может использовать его, даже если объекты типа menu не существуют. Следующая программа CLR_SCR.CPP использует метод clear_screen для очистки экрана дисплея:

#include

{ public: static void clear_screen(void); // Здесь должны быть другие методы private: int number_of_menu_options; };

void menu::clear_screen(void)

{ cout << "\033" << "}

Похожие публикации