Чтобы прояснить вопрос, я бы предпочел классифицировать использование ключевого слова static в трех разных формах:
(А). переменные
(В). функции
(С). переменные-члены / функции классов
объяснение следует ниже для каждого из подзаголовков:
(A) «статическое» ключевое слово для переменных
Это может быть немного сложно, однако, если объяснить и правильно понять, это довольно просто.
Чтобы объяснить это, во-первых, действительно полезно узнать о области действия, продолжительности и связях переменных, без которых вещи всегда трудно увидеть сквозь мутную концепцию staic ключевого слова.
1. Область действия : определяет, где в файле доступна переменная. Он может быть двух типов: (i) локальный или блочный . (ii) Глобальная сфера
2. Длительность : определяет, когда переменная создается и уничтожается. Опять же, он бывает двух типов: (i) Автоматическая продолжительность хранения (для переменных, имеющих локальную или блочную область видимости). (ii) Статическая продолжительность хранения (для переменных, имеющих глобальную область или локальные переменные (в функции или в блоке кода) со статическим спецификатором).
3. Связывание : определяет, может ли переменная быть доступна (или связана) в другом файле. Опять же (и, к счастью) это бывает двух типов: (i) Внутренняя связь
(для переменных, имеющих блочную область и глобальную область действия / область действия файла / глобальную область имен) (ii) Внешняя связь (для переменных, имеющих значение только для глобальной области действия / области действия файла /) Область глобального пространства имен)
Давайте рассмотрим пример ниже для лучшего понимания простых глобальных и локальных переменных (без локальных переменных со статической длительностью хранения):
//main file
#include <iostream>
int global_var1; //has global scope
const global_var2(1.618); //has global scope
int main()
{
//these variables are local to the block main.
//they have automatic duration, i.e, they are created when the main() is
// executed and destroyed, when main goes out of scope
int local_var1(23);
const double local_var2(3.14);
{
/* this is yet another block, all variables declared within this block are
have local scope limited within this block. */
// all variables declared within this block too have automatic duration, i.e,
/*they are created at the point of definition within this block,
and destroyed as soon as this block ends */
char block_char1;
int local_var1(32) //NOTE: this has been re-declared within the block,
//it shadows the local_var1 declared outside
std::cout << local_var1 <<"\n"; //prints 32
}//end of block
//local_var1 declared inside goes out of scope
std::cout << local_var1 << "\n"; //prints 23
global_var1 = 29; //global_var1 has been declared outside main (global scope)
std::cout << global_var1 << "\n"; //prints 29
std::cout << global_var2 << "\n"; //prints 1.618
return 0;
} //local_var1, local_var2 go out of scope as main ends
//global_var1, global_var2 go out of scope as the program terminates
//(in this case program ends with end of main, so both local and global
//variable go out of scope together
Теперь приходит концепция Linkage. Когда глобальная переменная, определенная в одном файле, предназначена для использования в другом файле, связь с переменной играет важную роль.
Связывание глобальных переменных задается ключевыми словами: (i) статическая и (ii) внешняя
(Теперь вы получите объяснение)
Ключевое слово static может применяться к переменным с локальной и глобальной областью действия, и в обоих случаях они означают разные вещи. Сначала я объясню использование ключевого слова «static» в переменных с глобальной областью действия (где я также поясню использование ключевого слова «extern»), а позже - для тех, которые имеют локальную область действия.
1. Статическое ключевое слово для переменных с глобальной областью видимости
Глобальные переменные имеют статическую длительность, то есть они не выходят за рамки, когда заканчивается конкретный блок кода (например, main ()), в котором он используется. В зависимости от связывания к ним можно получить доступ либо только в том же файле, в котором они объявлены (для статической глобальной переменной), либо вне файла, даже вне файла, в котором они объявлены (глобальные переменные типа extern)
В случае, когда глобальная переменная имеет спецификатор extern, и если к этой переменной обращаются вне файла, в котором она была инициализирована, она должна быть объявлена вперед в файле, где она используется, точно так же, как функция должна быть вперед объявляется, если его определение находится в файле, отличном от того, где он используется.
Напротив, если глобальная переменная имеет ключевое слово static, ее нельзя использовать в файле, за пределами которого она была объявлена.
(см. пример ниже для пояснения)
например:
//main2.cpp
static int global_var3 = 23; /*static global variable, cannot be
accessed in anyother file */
extern double global_var4 = 71; /*can be accessed outside this file linked to main2.cpp */
int main() { return 0; }
main3.cpp
//main3.cpp
#include <iostream>
int main()
{
extern int gloabl_var4; /*this variable refers to the gloabal_var4
defined in the main2.cpp file */
std::cout << global_var4 << "\n"; //prints 71;
return 0;
}
теперь любая переменная в c ++ может быть как константной, так и неконстантной, и для каждой «константности» мы получаем два случая связи по умолчанию с ++, в случае, если ни одна не указана:
(i) Если глобальная переменная не является константной, по умолчанию ее связь является внешней , то есть к неконстантной глобальной переменной можно получить доступ в другом файле .cpp путем предварительного объявления с использованием ключевого слова extern (другими словами, неконстантной глобальной переменные имеют внешнюю связь (со статической продолжительностью, конечно)). Также избыточное использование ключевого слова extern в исходном файле, в котором оно было определено. В этом случае, чтобы сделать неконстантную глобальную переменную недоступной для внешнего файла, используйте спецификатор «static» перед типом переменной .
(ii) Если глобальная переменная является const, ее связывание по умолчанию является статическим , то есть глобальная переменная const не может быть доступна в файле, отличном от того, где она определена (другими словами, глобальные переменные const имеют внутреннюю связь (со статической продолжительностью) конечно)). Также использование статического ключевого слова для предотвращения доступа к глобальной переменной const в другом файле является излишним. Здесь, чтобы глобальная переменная const имела внешнюю связь, используйте спецификатор extern перед типом переменной
Вот сводка для глобальных переменных области с различными связями
//globalVariables1.cpp
// defining uninitialized vairbles
int globalVar1; // uninitialized global variable with external linkage
static int globalVar2; // uninitialized global variable with internal linkage
const int globalVar3; // error, since const variables must be initialized upon declaration
const int globalVar4 = 23; //correct, but with static linkage (cannot be accessed outside the file where it has been declared*/
extern const double globalVar5 = 1.57; //this const variable ca be accessed outside the file where it has been declared
Далее мы исследуем, как ведут себя вышеупомянутые глобальные переменные при доступе в другом файле.
//using_globalVariables1.cpp (eg for the usage of global variables above)
// Forward declaration via extern keyword:
extern int globalVar1; // correct since globalVar1 is not a const or static
extern int globalVar2; //incorrect since globalVar2 has internal linkage
extern const int globalVar4; /* incorrect since globalVar4 has no extern
specifier, limited to internal linkage by
default (static specifier for const variables) */
extern const double globalVar5; /*correct since in the previous file, it
has extern specifier, no need to initialize the
const variable here, since it has already been
legitimately defined perviously */
2. Статическое ключевое слово для переменных с локальной областью действия
Обновления (август 2019 г.) статического ключевого слова для переменных в локальной области видимости
Это далее может быть разделено на две категории:
(i) статическое ключевое слово для переменных в функциональном блоке и (ii) статическое ключевое слово для переменных в неназванном локальном блоке.
(i) статическое ключевое слово для переменных в функциональном блоке.
Ранее я упоминал, что переменные с локальной областью действия имеют автоматическую длительность, то есть они начинают существовать при вводе блока (будь то нормальный блок, будь то функциональный блок) и перестают существовать, когда блок заканчивается, короче говоря, переменные с локальной областью действия имеют автоматическую длительность, а автоматические переменные длительности (и объекты) не имеют связи, означая, что они не видны вне блока кода.
Если статический спецификатор применяется к локальной переменной в функциональном блоке, он меняет продолжительность переменной с автоматического на статический, а его срок службы равен всей продолжительности программы, что означает, что она имеет фиксированное расположение в памяти, и ее значение инициализируется только один раз перед запуском программы, как указано в справочнике cpp (инициализация не должна быть перепутана с присваиванием)
Давайте посмотрим на пример.
//localVarDemo1.cpp
int localNextID()
{
int tempID = 1; //tempID created here
return tempID++; //copy of tempID returned and tempID incremented to 2
} //tempID destroyed here, hence value of tempID lost
int newNextID()
{
static int newID = 0;//newID has static duration, with internal linkage
return newID++; //copy of newID returned and newID incremented by 1
} //newID doesn't get destroyed here :-)
int main()
{
int employeeID1 = localNextID(); //employeeID1 = 1
int employeeID2 = localNextID(); // employeeID2 = 1 again (not desired)
int employeeID3 = newNextID(); //employeeID3 = 0;
int employeeID4 = newNextID(); //employeeID4 = 1;
int employeeID5 = newNextID(); //employeeID5 = 2;
return 0;
}
Глядя на приведенный выше критерий для статических локальных переменных и статических глобальных переменных, можно задаться вопросом, в чем может быть разница между ними. Хотя глобальные переменные доступны в любой точке кода (как в разных, так и в разных единицах перевода в зависимости от const -ness и extern -ness), статическая переменная, определенная в функциональном блоке, недоступна напрямую. Переменная должна быть возвращена значением функции или ссылкой. Давайте продемонстрируем это на примере:
//localVarDemo2.cpp
//static storage duration with global scope
//note this variable can be accessed from outside the file
//in a different compilation unit by using `extern` specifier
//which might not be desirable for certain use case.
static int globalId = 0;
int newNextID()
{
static int newID = 0;//newID has static duration, with internal linkage
return newID++; //copy of newID returned and newID incremented by 1
} //newID doesn't get destroyed here
int main()
{
//since globalId is accessible we use it directly
const int globalEmployee1Id = globalId++; //globalEmployeeId1 = 0;
const int globalEmployee2Id = globalId++; //globalEmployeeId1 = 1;
//const int employeeID1 = newID++; //this will lead to compilation error since newID++ is not accessible direcly.
int employeeID2 = newNextID(); //employeeID3 = 0;
int employeeID2 = newNextID(); //employeeID3 = 1;
return 0;
}
Более подробное объяснение о выборе статической глобальной и статической локальной переменной можно найти в этом потоке stackoverflow
(ii) статическое ключевое слово для переменных в неназванном локальном блоке.
Статические переменные в локальном блоке (не функциональном блоке) не могут быть доступны вне блока, как только локальный блок выходит из области видимости. Никаких предостережений к этому правилу.
//localVarDemo3.cpp
int main()
{
{
const static int static_local_scoped_variable {99};
}//static_local_scoped_variable goes out of scope
//the line below causes compilation error
//do_something is an arbitrary function
do_something(static_local_scoped_variable);
return 0;
}
В C ++ 11 введено ключевое слово, constexpr
которое гарантирует оценку выражения во время компиляции и позволяет компилятору оптимизировать код. Теперь, если значение статической константной переменной в области видимости известно во время компиляции, код оптимизируется аналогично тому, с которым constexpr
. Вот небольшой пример
Я рекомендую читателям также посмотреть разницу между переменными в потоке stackoverflowconstexpr
и static const
для них . На этом мое объяснение ключевого слова static применяется к переменным.
B. ключевое слово static для функций
в терминах функций ключевое слово static имеет прямое значение. Здесь это относится к связыванию функции.
Обычно все функции, объявленные в файле cpp, по умолчанию имеют внешнюю связь, то есть функция, определенная в одном файле, может использоваться в другом файле cpp путем предварительного объявления.
использование статического ключевого слова до объявления функции ограничивает его связь с внутренним , то есть статическая функция не может использоваться внутри файла вне ее определения.
C. Staitc Ключевое слово, используемое для переменных-членов и функций классов
1. «статическое» ключевое слово для переменных-членов классов
Я начну прямо с примера здесь
#include <iostream>
class DesignNumber
{
private:
static int m_designNum; //design number
int m_iteration; // number of iterations performed for the design
public:
DesignNumber() { } //default constructor
int getItrNum() //get the iteration number of design
{
m_iteration = m_designNum++;
return m_iteration;
}
static int m_anyNumber; //public static variable
};
int DesignNumber::m_designNum = 0; // starting with design id = 0
// note : no need of static keyword here
//causes compiler error if static keyword used
int DesignNumber::m_anyNumber = 99; /* initialization of inclass public
static member */
enter code here
int main()
{
DesignNumber firstDesign, secondDesign, thirdDesign;
std::cout << firstDesign.getItrNum() << "\n"; //prints 0
std::cout << secondDesign.getItrNum() << "\n"; //prints 1
std::cout << thirdDesign.getItrNum() << "\n"; //prints 2
std::cout << DesignNumber::m_anyNumber++ << "\n"; /* no object
associated with m_anyNumber */
std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 100
std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 101
return 0;
}
В этом примере статическая переменная m_designNum сохраняет свое значение, и эта единственная закрытая переменная-член (потому что она статическая) является общей для всех переменных типа объекта DesignNumber.
Также как и другие переменные-члены, статические переменные-члены класса не связаны ни с одним объектом класса, что демонстрируется печатью anyNumber в основной функции.
const против неконстантных статических переменных-членов в классе
(i) статические переменные-члены неконстантного класса.
В предыдущем примере статические члены (как открытые, так и частные) были неконстантными. Стандарт ISO запрещает инициализацию неконстантных статических элементов в классе. Следовательно, как и в предыдущем примере, они должны быть инициализированы после определения класса, с оговоркой, что статическое ключевое слово должно быть опущено
(ii) const-статические переменные-члены класса
это просто и соответствует соглашению об инициализации других переменных-членов const, то есть постоянные статические переменные-члены класса могут быть инициализированы в точке объявления, и они могут быть инициализированы в конце объявления класса с одним предупреждением о том, что ключевое слово const необходимо добавить к статическому члену при инициализации после определения класса.
Однако я бы порекомендовал инициализировать статические переменные-члены const в точке объявления. Это соответствует стандартному соглашению C ++ и делает код более чистым
Дополнительные примеры статических переменных-членов в классе можно найти по следующей ссылке с learncpp.com
http://www.learncpp.com/cpp-tutorial/811-static-member-variables/
2. «статическое» ключевое слово для функции-члена классов
Как переменные-члены классов могут быть статическими, так и функции-члены классов. Обычные функции-члены классов всегда связаны с объектом типа класса. Напротив, статические функции-члены класса не связаны ни с одним объектом класса, т. Е. У них нет * этого указателя.
Во-вторых, поскольку статические функции-члены класса не имеют * этого указателя, их можно вызывать с помощью оператора разрешения имени и области видимости в главной функции (ClassName :: functionName ();)
В-третьих, статические функции-члены класса могут обращаться только к статическим переменным-членам класса, поскольку нестатические переменные-члены класса должны принадлежать объекту класса.
Дополнительные примеры статических функций-членов в классе можно найти по следующей ссылке с learncpp.com.
http://www.learncpp.com/cpp-tutorial/812-static-member-functions/