Статическое ключевое слово и его различные применения в С++

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

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

Стандарт С++ говорит об этом для членов данных класса с ключевым словом static:

3.7.1 Статическая продолжительность хранения [basic.stc.static]

3 Ключевое слово static может использоваться для объявления локальной переменной со статической продолжительностью хранения.

4 Ключевое слово static, примененное к элементу данных класса в определении класса, дает время статического хранения элемента данных.

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

Он также говорит только о продолжительности хранения в отношении членов класса, а о том, что он не является специфичным для экземпляра, а также свойство static no? Или это срок хранения?

Как насчет случая с static и объемом файла? По умолчанию считается, что все глобальные переменные имеют статическую продолжительность хранения? Следующее (из раздела 3.7.1), по-видимому, указывает на следующее:

1 Все переменные, которые не имеют динамической продолжительности хранения, не имеют продолжительности хранения потоков и имеют не локальный статический период хранения. Хранилище для этих объектов должно длиться в течение всего времени программы (3.6.2, 3.6.3)

Как static относится к привязке переменной?

Все это ключевое слово static совершенно сбивает с толку, может ли кто-то прояснить различные варианты использования этого языка на английском языке, а также указать, когда инициализировать член класса static?

Ответ 1

Переменные:

static переменные существуют для "времени жизни" единицы перевода, в которой она определена, и:

  • Если он находится в области имен (то есть вне функций и классов), то к нему нельзя получить доступ из любого другого модуля перевода. Это известно как "внутренняя связь" или "статическая продолжительность хранения". (Не делайте этого в заголовках, за исключением constexpr. Что-нибудь еще, и в результате вы получите отдельную переменную в каждой единице перевода, что безумно запутанно)
  • Если это переменная в функции, к ней нельзя получить доступ извне функции, как и к любой другой локальной переменной. (это локальные они упоминали)
  • члены класса не имеют ограниченной области видимости из-за static, но могут быть адресованы как из класса, так и из экземпляра (например, std::string::npos). [Примечание: вы можете объявить статические члены в классе, но обычно они все равно должны быть определены в модуле перевода (файл cpp), и, как таковой, там только один на класс]

местоположения как код:

static std::string namespaceScope = "Hello";
void foo() {
    static std::string functionScope= "World";
}
struct A {
   static std::string classScope = "!";
};

Перед выполнением какой-либо функции в модуле перевода (возможно, после начала main выполнения) переменные со статической продолжительностью хранения (область пространства имен) в этом модуле преобразования будут "инициализированы константой" (для constexpr где это возможно, или равны нулю в противном случае), а затем нелокалы должным образом "динамически инициализируются" в том порядке, в котором они определены в модуле перевода (для таких вещей, как std::string="HI"; которые не являются constexpr). Наконец, локальные статические функции будут инициализированы, когда в первый раз выполнение "достигнет" строки, где они объявлены. Все static переменные уничтожаются в обратном порядке инициализации.

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

T& get_global() {
    static T global = initial_value();
    return global;
}

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

функции

Значительно проще, static часто используется в качестве функции-члена класса, и очень редко используется для отдельно стоящей функции.

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

struct A {
    A() {++A_count;}
    A(const A&) {++A_count;}
    A(A&&) {++A_count;}
    ~A() {--A_count;}

    static int get_count() {return A_count;}
private:
    static int A_count;
}

int main() {
    A var;

    int c0 = var.get_count(); //some compilers give a warning, but it ok.
    int c1 = A::get_count(); //normal way
}

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

  • Может использоваться в файле cpp, чтобы гарантировать, что функция никогда не используется из любого другого файла.
  • Может быть помещен в заголовок, и у каждого файла будет своя копия функции. Не полезно, так как inline делает почти то же самое.
  • Ускоряет время соединения за счет сокращения работы
  • Можно поместить функцию с одинаковым именем в каждую единицу перевода, и все они могут делать разные вещи. Например, вы могли бы поместить static void log(const char*) {} в каждый файл cpp, и каждый из них мог бы каждый регистрироваться по-своему.

Ответ 2

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

Связь является ортогональной к этому.

Я думаю, что это самое важное различие, которое вы можете сделать. Понимать это и все остальное, а также помнить об этом должно быть легко (не обращаясь непосредственно к @Tony, но к тому, кто бы мог прочитать это в будущем).

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

Что это значит с локальной переменной? Это локальная переменная функции?

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

А как насчет случая со статической и файловой областью? Все ли глобальные переменные по умолчанию имеют статическую продолжительность хранения?

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

Как статические связаны со связью переменной?

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

Разберем все это:

//

static int x; //internal linkage
              //non-static storage - each translation unit will have its own copy of x
              //NOT A TRUE GLOBAL!

int y;        //static storage duration (can be used with extern)
              //actual global
              //external linkage
struct X
{
   static int x;     //static storage duration - shared between class instances 
};

void foo()
{
   static int x;     //static storage duration - shared between calls
}

Это целое статическое ключевое слово совершенно сбивает с толку

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

Ответ 3

Чтобы прояснить вопрос, я бы классифицировал использование ключевого слова static в трех разных формах:

(А). переменные

(В). функции

(С). переменные-члены/функции классов

объяснение следует ниже для каждого из подзаголовков:

(A) ключевое слово static для переменных

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

Чтобы объяснить это, сначала полезно узнать о объеме, продолжительности  и связывание переменных, без которых вещи всегда трудно увидеть  через мрачную концепцию 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++ может быть константной или неконстантной, и для каждого 'const -ness' мы получаем два случая связи по умолчанию 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 reference(инициализацию не следует путать с присваиванием)

Давайте посмотрим на пример.

//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 does not 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 does not 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;
}

Более подробное объяснение о выборе статической глобальной и статической локальной переменных можно найти в этой теме потока стекового потока

.(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. Вот небольшой пример

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

B. ключевое слово "static", используемое для функций

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

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

C. Staitc Ключевое слово, используемое для переменных-членов и функций классов

1. ключевое слово "static" для переменных-членов классов

Я начну непосредственно с примера здесь

#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 в основной функции

константные и неконстантные статические переменные-члены в классе

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

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

Однако я бы порекомендовал инициализировать статические переменные-члены const в точке объявления. Это соответствует стандартному соглашению c++ и делает код более чистым

Дополнительные примеры статических переменных-членов в классе можно найти по следующей ссылке с learncpp.com. http://www.learncpp.com/cpp-tutorial/811-static-member-variables/

2. ключевое слово "static" для функции-члена классов

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

Во-вторых, поскольку статические функции-члены класса не имеют * этого указателя, их можно вызывать с помощью оператора разрешения имени и области видимости в основной функции (ClassName :: functionName();)

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

Дополнительные примеры статических функций-членов в классе можно найти по следующей ссылке с learncpp.com.

http://www.learncpp.com/cpp-tutorial/812-static-member-functions/

Ответ 4

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

int myFun()
{
static int i=5;
i++;
return i;
}
int main()
{
printf("%d", myFun());
printf("%d", myFun());
printf("%d", myFun());
}

покажет 678 вместо 666, потому что он запоминает добавочное значение.

Что касается статических членов, они сохраняют свое значение в экземплярах класса. Итак, следующий код:

struct A
{
static int a;
};
int main()
{
A first;
A second;
first.a = 3;
second.a = 4;
printf("%d", first.a);
}

будет печатать 4, так как first.a и second.a по существу являются одной и той же переменной. Что касается инициализации, см. этот вопрос.

Ответ 5

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

a.cpp

static int x = 7;

void printax()
{
    cout << "from a.cpp: x=" << x << endl;
}

b.cpp

static int x = 9;

void printbx()
{
    cout << "from b.cpp: x=" << x << endl;
}

main.cpp:

int main(int, char **)
{
    printax(); // Will print 7
    printbx(); // Will print 9

    return 0;
}

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

unsigned int powersoftwo()
{
    static unsigned lastpow;

    if(lastpow == 0)
        lastpow = 1;
    else
        lastpow *= 2;

    return lastpow;
}

int main(int, char **)
{
    for(int i = 0; i != 10; i++)
        cout << "2^" << i << " = " << powersoftwo() << endl;
}

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

class Test
{
private:
    static char *xxx;

public:
    static int yyy;

public:
    Test()
    {        
        cout << this << "The static class variable xxx is at address "
             << static_cast<void *>(xxx) << endl;
        cout << this << "The static class variable yyy is at address "
             << static_cast<void *>(&y) << endl;
    }
};

// Necessary for static class variables.
char *Test::xxx = "I'm Triple X!";
int Test::yyy = 0;

int main(int, char **)
{
    Test t1;
    Test t2;

    Test::yyy = 666;

    Test t3;
};

Маркировка неклассовой функции как static делает эту функцию доступной только из этого файла и недоступной из других файлов.

a.cpp

static void printfilename()
{ // this is the printfilename from a.cpp - 
  // it can't be accessed from any other file
    cout << "this is a.cpp" << endl;
}

b.cpp

static void printfilename()
{ // this is the printfilename from b.cpp - 
  // it can't be accessed from any other file
    cout << "this is b.cpp" << endl;
}

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

class Test
{
private:
    static int count;

public:
    static int GetTestCount()
    {
        return count;
    };

    Test()
    {
        cout << this << "Created an instance of Test" << endl;
        count++;
    }

    ~Test()
    {
        cout << this << "Destroyed an instance of Test" << endl;
        count--;
    }
};

int Test::count = 0;

int main(int, char **)
{
    Test *arr[10] = { NULL };

    for(int i = 0; i != 10; i++)
        arr[i] = new Test();

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    // now, delete them all except the first and last!
    for(int i = 1; i != 9; i++)
        delete arr[i];        

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[0];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[9];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    return 0;
}

Ответ 6

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

class MyClass
{
    public:
    int myVar; 
    static int myStaticVar;
};

//Static member variables must be initialized. Unless you're using C++11, or it an integer type,
//they have to be defined and initialized outside of the class like this:
MyClass::myStaticVar = 0;

MyClass classA;
MyClass classB;

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

MyClass::myStaticVar //Assuming it publicly accessible.

При использовании внутри функции как локальной переменной (а не как члена-члена класса) статическое ключевое слово делает что-то другое. Он позволяет создавать постоянную переменную, не предоставляя глобальную область.

int myFunc()
{
   int myVar = 0; //Each time the code reaches here, a new variable called 'myVar' is initialized.
   myVar++;

   //Given the above code, this will *always* print '1'.
   std::cout << myVar << std::endl;

   //The first time the code reaches here, 'myStaticVar' is initialized. But ONLY the first time.
   static int myStaticVar = 0;

   //Each time the code reaches here, myStaticVar is incremented.
   myStaticVar++;

   //This will print a continuously incrementing number,
   //each time the function is called. '1', '2', '3', etc...
   std::cout << myStaticVar << std::endl;
}

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

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

class MyClass
{
    public:
    int Func()
    {
        //...do something...
    }

    static int StaticFunc()
    {
        //...do something...
    }
};

int main()
{
   MyClass myClassA;
   myClassA.Func(); //Calls 'Func'.
   myClassA.StaticFunc(); //Calls 'StaticFunc'.

   MyClass::StaticFunc(); //Calls 'StaticFunc'.
   MyClass::Func(); //Error: You can't call a non-static member-function without a class instance!

   return 0;
}

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

Ответ 7

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

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
private:
    void somePrivateMethod();
};

Когда вы создаете экземпляр этого класса в своем Main, вы делаете что-то вроде этого.

int main()
{
   A a1;
   //do something on a1
   A a2;
   //do something on a2
}

Эти два экземпляра класса полностью отличаются друг от друга и работают независимо друг от друга. Но если вы должны воссоздать класс A, как это.

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
    static int x;
private:
    void somePrivateMethod();
};

Возврат к главному снова.

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   A a2;
   a2.x++;
   //do something on a2
}

Тогда a1 и a2 будут иметь одну и ту же копию int x, в которой любые операции над x в a1 будут непосредственно влиять на операции x в a2. Поэтому, если бы я сделал это

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   cout << a1.x << endl; //this would be 1
   A a2;
   a2.x++;
   cout << a2.x << endl; //this would be 2 
   //do something on a2
}

Оба экземпляра класса A разделяют статические переменные и функции. Надеюсь, что это ответ на ваш вопрос. Мое ограниченное знание C позволяет мне сказать, что определение функции или переменной как статического означает, что только файл виден только в том, что функция или переменная определяется как статическая. Но на это лучше ответить парень C, а не я. С++ позволяет как C, так и С++ образом объявлять ваши переменные как статические, поскольку он полностью обратно совместим с C.

Ответ 8

Что это означает с локальной переменной? Является ли это локальной переменной функции?

Да - неглобальный, например, локальная переменная функции.

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

Right.

Он также говорит только о продолжительности хранения в отношении членов класса, а о том, что он не является специфичным для экземпляра, а также свойство static no? Или это срок хранения?

class R { static int a; }; // << static lives for the duration of the program

то есть все экземпляры R share int R::a - int R::a никогда не копируются.

Как насчет случая со статическими и файловыми областями?

Фактически глобальный, который имеет конструктор/деструктор, где это необходимо - инициализация не откладывается до доступа.

Как статичность связана с привязкой переменной?

Для локальной функции она является внешней. Доступ: он доступен для функции (если, конечно, вы не вернете ее).

Для класса он является внешним. Доступ: применяются спецификаторы стандартного доступа (общедоступные, защищенные, частные).

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

Все это статическое ключевое слово совершенно путается

В С++ слишком много целей.

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

Он автоматически инициализируется перед main, если он загружен и имеет конструктор. Это может показаться хорошей вещью, но порядок инициализации в значительной степени не под вашим контролем, поэтому сложная инициализация становится очень сложной для поддержания, и вы хотите свести ее к минимуму - если у вас есть статичность, тогда функции локальных масштабов намного лучше в библиотеках и проекты. Что касается данных со статической продолжительностью хранения, вы должны попытаться свести к минимуму этот проект, особенно если он изменен (глобальные переменные). Инициализация "время" также варьируется по ряду причин: загрузчик и ядро ​​имеют некоторые трюки для минимизации следов памяти и отсрочки инициализации, в зависимости от данных, о которых идет речь.

Ответ 9

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

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

Попробуем следующий пример, чтобы понять концепцию статических членов данных:

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects.
   cout << "Total objects: " << Box::objectCount << endl;

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Constructor called.
Constructor called.
Total objects: 2

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

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

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

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

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{

   // Print total number of objects before creating object.
   cout << "Inital Stage Count: " << Box::getCount() << endl;

   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects after creating object.
   cout << "Final Stage Count: " << Box::getCount() << endl;

   return 0;
}

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2