La palabra clave estática y sus diversos usos en C ++

195

La palabra clave statices una que tiene varios significados en C ++ que me parecen muy confusos y nunca puedo cambiar mi mente sobre cómo se supone que funciona.

Por lo que entiendo, hay staticuna duración de almacenamiento, lo que significa que dura toda la vida del programa en el caso de un global, pero cuando se trata de un local, significa que se inicializa cero de forma predeterminada.

El estándar C ++ dice esto para los miembros de datos de clase con la palabra clave static:

3.7.1 Duración del almacenamiento estático [basic.stc.static]

3 La palabra clave static puede usarse para declarar una variable local con duración de almacenamiento estático.

4 La palabra clave static aplicada a un miembro de datos de clase en una definición de clase proporciona al miembro de datos la duración del almacenamiento estático.

¿Qué significa con la variable local ? ¿Es esa una variable local de función? Porque también hay que cuando declaras una función local como staticque solo se inicializa una vez, la primera vez que ingresa a esta función.

También solo habla sobre la duración del almacenamiento con respecto a los miembros de la clase, ¿qué pasa si no es específico de la instancia, eso también es una propiedad de staticno? ¿O es la duración del almacenamiento?

Ahora, ¿qué pasa con el caso staticy el alcance del archivo? ¿Se considera que todas las variables globales tienen una duración de almacenamiento estático por defecto? Lo siguiente (de la sección 3.7.1) parece indicarlo:

1 Todas las variables que no tienen una duración de almacenamiento dinámico, no tienen una duración de almacenamiento de subprocesos y no son locales tienen una duración de almacenamiento estático. El almacenamiento para estas entidades durará la duración del programa (3.6.2, 3.6.3)

¿Cómo se staticrelaciona con el enlace de una variable?

Toda esta staticpalabra clave es francamente confusa, ¿alguien puede aclarar los diferentes usos del inglés y también decirme cuándo inicializar a un staticmiembro de la clase?

Tony el león
fuente
relacionado: stackoverflow.com/questions/572547/what-does-static-mean-in-c
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

147

Variables:

staticexisten variables para la "vida útil" de la unidad de traducción en la que se define , y:

  • Si está en un ámbito de espacio de nombres (es decir, fuera de las funciones y clases), no se puede acceder desde ninguna otra unidad de traducción. Esto se conoce como "enlace interno" o "duración de almacenamiento estático". (No haga esto en los encabezados, excepto constexpr. Cualquier otra cosa, y terminará con una variable separada en cada unidad de traducción, lo cual es una locura confusa)
  • Si es una variable en una función , no se puede acceder desde fuera de la función, como cualquier otra variable local. (este es el local que mencionaron)
  • los miembros de la clase no tienen un alcance restringido debido a static, pero pueden abordarse desde la clase, así como desde una instancia (como std::string::npos). [Nota: puede declarar miembros estáticos en una clase, pero generalmente deben definirse en una unidad de traducción (archivo cpp) y, como tal, solo hay uno por clase]

ubicaciones como código:

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

Antes de que se ejecute cualquier función en una unidad de traducción (posiblemente después de maincomenzar la ejecución), las variables con duración de almacenamiento estático (alcance del espacio de nombres) en esa unidad de traducción se "inicializarán constantemente" ( constexprdonde sea posible, o cero de lo contrario), y luego no los locales se "inicializan dinámicamente" correctamente en el orden en que se definen en la unidad de traducción (para cosas como std::string="HI";esas no constexpr). Finalmente, las estadísticas locales de función se inicializarán la primera vez que la ejecución "llegue" a la línea donde se declaran. Todas las staticvariables destruidas en el orden inverso de inicialización.

La forma más fácil de hacer todo esto bien es hacer que todas las variables estáticas que no se constexprinicializan se conviertan en locales estáticos de función, lo que garantiza que todas sus estáticas / globales se inicialicen correctamente cuando intente usarlas sin importar qué, evitando así la inicialización estática orden fiasco .

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

Tenga cuidado, porque cuando la especificación dice que las variables de ámbito de espacio de nombres tienen "duración de almacenamiento estático" por defecto, significan el bit de "vida útil de la unidad de traducción", pero eso no significa que no se pueda acceder fuera del archivo.

Las funciones

Significativamente más directo, a staticmenudo se usa como una función de miembro de clase, y muy raramente se usa para una función independiente.

Una función miembro estática difiere de una función miembro regular en que puede llamarse sin una instancia de una clase, y como no tiene instancia, no puede acceder a miembros no estáticos de la clase. Las variables estáticas son útiles cuando desea tener una función para una clase que definitivamente no se refiere a ningún miembro de instancia, o para administrar staticvariables de miembro.

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's ok.
    int c1 = A::get_count(); //normal way
}

Una staticfunción libre significa que ninguna otra unidad de traducción hará referencia a la función y, por lo tanto, el enlazador puede ignorarla por completo. Esto tiene una pequeña cantidad de propósitos:

  • Se puede usar en un archivo cpp para garantizar que la función nunca se use desde ningún otro archivo.
  • Se puede poner en un encabezado y cada archivo tendrá su propia copia de la función. No es útil, ya que en línea hace más o menos lo mismo.
  • Acelera el tiempo de enlace al reducir el trabajo
  • Puede poner una función con el mismo nombre en cada unidad de traducción, y todos pueden hacer cosas diferentes. Por ejemplo, podría poner un static void log(const char*) {}en cada archivo cpp, y cada uno podría iniciar sesión de una manera diferente.
Pato mugido
fuente
1
¿Qué pasa con los miembros de la clase? ¿No es un tercer caso separado?
Étienne
44
@ Etienne: los miembros de datos de clase estática son iguales a las variables globales estáticas, excepto que puede acceder a ellos desde otras unidades de traducción, y cualquier acceso (excepto las funciones de miembro) debe especificar el classname::alcance. Las funciones de miembro de clase estática son como funciones globales pero con alcance a la clase, o como miembros normales pero sin this(eso no es una opción, esas dos deberían ser equivalentes).
Steve314
1
@LuchianGrigore: aunque veo tu punto, no estoy seguro de qué palabras usar.
Mooing Duck
1
@ Steve314: Entiendo lo que quieres decir, pero cuando se trata de un término tan terriblemente sobrecargado como estático , desearía que todos fuéramos un poco más cuidadosos. En particular, todas las variables globales (realmente el nivel del espacio de nombres) tienen una duración estática, por lo que se puede entender que agregar estático en variables globales estáticasnamespace A { static int x; } , lo que significa vinculación interna y es muy diferente del comportamiento de los miembros de datos de clase estática .
David Rodríguez - dribeas
1
"Si está en un ámbito de espacio de nombres, entonces no se puede acceder desde ninguna otra unidad de traducción ..." ¿Qué quiere decir si está en un ámbito de espacio de nombres? ¿No es siempre así, podría dar un ejemplo y un contraejemplo?
AturSams
66

La duración del almacenamiento estático significa que la variable reside en el mismo lugar en la memoria durante la vida útil del programa.

El enlace es ortogonal a esto.

Creo que esta es la distinción más importante que puedes hacer. Comprender esto y el resto, así como recordarlo, debería ser fácil (no dirigirse a @Tony directamente, sino a cualquiera que pueda leer esto en el futuro).

La palabra clave staticse puede usar para denotar enlaces internos y almacenamiento estático, pero en esencia estos son diferentes.

¿Qué significa con la variable local? ¿Es esa una variable local de función?

Si. Independientemente de cuándo se inicializa la variable (en la primera llamada a la función y cuando la ruta de ejecución alcanza el punto de declaración), residirá en el mismo lugar en la memoria durante la vida del programa. En este caso, staticle da almacenamiento estático.

Ahora, ¿qué pasa con el caso con alcance estático y de archivo? ¿Se considera que todas las variables globales tienen una duración de almacenamiento estático por defecto?

Sí, todos los globales tienen, por definición, una duración de almacenamiento estático (ahora que aclaramos lo que eso significa). Pero las variables con ámbito de espacio de nombres no se declaran con static, porque eso les daría un enlace interno, por lo que una variable por unidad de traducción.

¿Cómo se relaciona la estática con el enlace de una variable?

Proporciona un enlace interno de variables con ámbito de espacio de nombres. Da a los miembros y las variables locales la duración del almacenamiento estático.

Vamos a ampliar todo esto:

//

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
}

Toda esta palabra clave estática es francamente confusa.

Definitivamente, a menos que estés familiarizado con eso. :) Al tratar de evitar agregar nuevas palabras clave al idioma, el comité reutilizó esta, OMI, a este efecto: confusión. Se usa para significar cosas diferentes (podría decir, probablemente cosas opuestas).

Luchian Grigore
fuente
1
Déjame aclarar esto: ¿estás diciendo que cuando digo static int xen el ámbito del espacio de nombres, eso le da almacenamiento no estático ?
Michael Hagar
30

Para aclarar la pregunta, preferiría clasificar el uso de la palabra clave 'estática' en tres formas diferentes:

(UNA). variables

(SI). funciones

(C). variables miembro / funciones de clases

la explicación sigue a continuación para cada uno de los subtítulos:

(A) palabra clave 'estática' para variables

Este puede ser un poco complicado, sin embargo, si se explica y comprende correctamente, es bastante sencillo.

Para explicar esto, primero es realmente útil saber sobre el alcance, la duración y la vinculación de las variables, sin las cuales las cosas siempre son difíciles de ver a través del concepto turbio de palabra clave estática

1. Alcance : determina dónde está accesible la variable en el archivo. Puede ser de dos tipos: (i) Alcance local o de bloque . (ii) Alcance global

2. Duración : determina cuándo se crea y destruye una variable. De nuevo, es de dos tipos: (i) Duración automática de almacenamiento (para variables que tienen alcance local o de bloque). (ii) Duración del almacenamiento estático (para variables que tienen alcance global o variables locales (en una función o en un bloque de código) con especificador estático ).

3. Vinculación : determina si se puede acceder (o vincular) a una variable en otro archivo. Nuevamente (y afortunadamente) es de dos tipos: (i) Enlace interno (para variables que tienen alcance de bloque y alcance global / alcance de archivo / alcance de espacio de nombres global) (ii) enlace externo (para variables que tienen solo alcance global / alcance de archivo / Alcance del espacio de nombres global)

Veamos un ejemplo a continuación para comprender mejor las variables globales y locales simples (no hay variables locales con duración de almacenamiento estático):

//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

Ahora viene el concepto de vinculación. Cuando una variable global definida en un archivo está destinada a ser utilizada en otro archivo, el enlace de la variable juega un papel importante.

El enlace de variables globales se especifica mediante las palabras clave: (i) estático y (ii) externo

(Ahora obtienes la explicación)

La palabra clave estática se puede aplicar a variables con alcance local y global, y en ambos casos, significan cosas diferentes. Primero explicaré el uso de la palabra clave 'estática' en variables con alcance global (donde también aclararé el uso de la palabra clave 'extern') y más tarde para aquellos con alcance local.

1. Palabra clave estática para variables con alcance global

Las variables globales tienen una duración estática, lo que significa que no quedan fuera del alcance cuando finaliza un bloque de código particular (por ejemplo, main ()) en el que se usa. Dependiendo de la vinculación, se puede acceder a ellos solo dentro del mismo archivo donde se declaran (para la variable global estática) o fuera del archivo, incluso fuera del archivo en el que se declaran (variables globales de tipo externo)

En el caso de una variable global que tenga un especificador externo, y si se accede a esta variable fuera del archivo en el que se ha inicializado, debe declararse hacia adelante en el archivo donde se está utilizando, al igual que una función debe ser hacia adelante declarado si su definición está en un archivo diferente de donde se está utilizando.

Por el contrario, si la variable global tiene una palabra clave estática, no se puede usar en un archivo fuera del cual se ha declarado.

(ver ejemplo a continuación para aclaraciones)

p.ej:

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

ahora cualquier variable en c ++ puede ser const o no const y para cada 'const-ness' obtenemos dos casos de enlace por defecto de c ++, en caso de que no se especifique ninguno:

(i) Si una variable global no es constante, su vinculación es externa por defecto , es decir, se puede acceder a la variable global no constante en otro archivo .cpp mediante declaración directa utilizando la palabra clave externa (en otras palabras, no global constante las variables tienen enlace externo (con duración estática, por supuesto)). Además, el uso de palabras clave externas en el archivo original donde se ha definido es redundante. En este caso, para hacer que una variable global no constante sea inaccesible a un archivo externo, use el especificador 'estático' antes del tipo de la variable .

(ii) Si una variable global es constante, su vinculación es estática por defecto , es decir, no se puede acceder a una variable global constante en un archivo que no sea donde se define (en otras palabras, las variables globales constantes tienen vinculación interna (con duración estática) por supuesto)). También es redundante el uso de palabras clave estáticas para evitar el acceso a una variable global constante en otro archivo. Aquí, para hacer que una variable global constante tenga un enlace externo, use el especificador 'extern' antes del tipo de la variable

Aquí hay un resumen de las variables de alcance global con varios enlaces.

//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

A continuación, investigamos cómo se comportan las variables globales anteriores cuando se accede a ellas en un archivo diferente.

//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. Palabra clave estática para variables con alcance local

Actualizaciones (agosto de 2019) sobre palabras clave estáticas para variables en ámbito local

Esto se puede subdividir en dos categorías:

(i) palabra clave estática para variables dentro de un bloque de funciones , y (ii) palabra clave estática para variables dentro de un bloque local sin nombre.

(i) palabra clave estática para variables dentro de un bloque de funciones.

Anteriormente, mencioné que las variables con alcance local tienen una duración automática, es decir, llegan a existir cuando se ingresa el bloque (ya sea un bloque normal, sea un bloque de función) y dejan de existir cuando el bloque termina, en pocas palabras, variables con alcance local tienen duración automática y las variables de duración automática (y los objetos) no tienen vinculación, lo que significa que no son visibles fuera del bloque de código.

Si el especificador estático se aplica a una variable local dentro de un bloque de funciones, cambia la duración de la variable de automática a estática y su tiempo de vida es la duración completa del programa, lo que significa que tiene una ubicación de memoria fija y su valor solo se inicializa una vez antes del inicio del programa como se menciona en la referencia de cpp (la inicialización no debe confundirse con la asignación)

Veamos un ejemplo.

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

Al observar el criterio anterior para las variables locales estáticas y las variables globales estáticas, uno podría verse tentado a preguntar cuál podría ser la diferencia entre ellas. Mientras que las variables globales son accesibles en cualquier punto dentro del código (en la misma unidad de traducción, así como en diferentes unidades, dependiendo de la const -ness y extern -ness), una variable estática definida dentro de un bloque de funciones no es directamente accesible. La variable tiene que ser devuelta por el valor de la función o referencia. Vamos a demostrar esto con un ejemplo:

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

Se puede encontrar más explicaciones sobre la elección de la variable estática global y local estática en este hilo de stackoverflow

(ii) palabra clave estática para variables dentro de un bloque local sin nombre.

No se puede acceder a las variables estáticas dentro de un bloque local (no un bloque de función) fuera del bloque una vez que el bloque local queda fuera de alcance. No hay advertencias a esta regla.

    //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 introdujo la palabra clave constexprque garantiza la evaluación de una expresión en tiempo de compilación y permite al compilador optimizar el código. Ahora, si el valor de una variable constante estática dentro de un ámbito se conoce en tiempo de compilación, el código se optimiza de una manera similar a la que tiene constexpr. Aquí hay un pequeño ejemplo

Recomiendo a los lectores que también busquen la diferencia entre constexpry static constpara las variables en este hilo de stackoverflow . Esto concluye mi explicación de la palabra clave estática aplicada a las variables.

B. palabra clave 'estática' utilizada para funciones

En términos de funciones, la palabra clave estática tiene un significado directo. Aquí, se refiere al enlace de la función Normalmente, todas las funciones declaradas dentro de un archivo cpp tienen enlace externo por defecto, es decir, una función definida en un archivo puede usarse en otro archivo cpp mediante declaración directa.

el uso de una palabra clave estática antes de que la declaración de función limite su vinculación a interna , es decir, una función estática no se puede usar dentro de un archivo fuera de su definición.

C. Palabra clave Staitc utilizada para variables miembro y funciones de clases

1. palabra clave 'estática' para variables miembro de clases

Empiezo directamente con un ejemplo aquí

#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;
}

En este ejemplo, la variable estática m_designNum conserva su valor y esta variable de miembro privado único (porque es estática) se comparte en blanco y negro con todas las variables del tipo de objeto DesignNumber

Además, como otras variables miembro, las variables miembro estáticas de una clase no están asociadas con ningún objeto de clase, lo que se demuestra al imprimir anyNumber en la función principal

variables miembro estáticas const vs no const en clase

(i) variables de miembro estático de clase no constante En el ejemplo anterior, los miembros estáticos (públicos y privados) no eran constantes. El estándar ISO prohíbe que los miembros estáticos no constantes se inicialicen en la clase. Por lo tanto, como en el ejemplo anterior, deben inicializarse después de la definición de clase, con la advertencia de que la palabra clave estática debe omitirse

(ii) variables miembro const-static de clase esto es sencillo y va con la convención de la inicialización de otras variables miembro const, es decir, las variables miembro const static de una clase se pueden inicializar en el punto de declaración y se pueden inicializar al final de la declaración de clase con una advertencia de que la palabra clave const debe agregarse al miembro estático cuando se inicializa después de la definición de clase.

Sin embargo, recomendaría inicializar las variables miembro estáticas constantes en el punto de declaración. Esto va con la convención estándar de C ++ y hace que el código se vea más limpio

Para obtener más ejemplos sobre variables miembro estáticas en una clase, busque el siguiente enlace en learncpp.com http://www.learncpp.com/cpp-tutorial/811-static-member-variables/

2. palabra clave 'estática' para la función miembro de clases

Al igual que las variables miembro de las clases pueden ser estáticas, también pueden las funciones miembro de las clases. Las funciones miembro normales de las clases siempre están asociadas con un objeto del tipo de clase. Por el contrario, las funciones miembro estáticas de una clase no están asociadas con ningún objeto de la clase, es decir, no tienen * este puntero.

En segundo lugar, dado que las funciones miembro estáticas de la clase no tienen * este puntero, pueden llamarse utilizando el operador de resolución de alcance y nombre de clase en la función principal (ClassName :: functionName ();)

En tercer lugar, las funciones miembro estáticas de una clase solo pueden acceder a las variables miembro estáticas de una clase, ya que las variables miembro no estáticas de una clase deben pertenecer a un objeto de clase.

Para obtener más ejemplos sobre funciones miembro estáticas en una clase, consulte el siguiente enlace de learncpp.com

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

ggulgulia
fuente
1
1) Antes de c ++ 17, solo se pueden inicializar en la clase las variables integrales integrales estáticas, por ejemplo, struct Foo{static const std::string name = "cpp";};es un error, namedebe definirse fuera de la clase; con variables en línea introducidas en c ++ 17 se puede codificar: struct Foo{static inline const std::string name = "cpp";};2) Se puede acceder a las funciones miembro / miembro estático público por nombre de clase con operador de resolución de alcance y también una instancia con operador de punto (por ejemplo: instance.some_static_method ())
oz1
¿No debería "m_anyVariable" convertirse en "m_anyNumber"? en tu último código de ejemplo?
gebbissimo
No puedo juzgar la integridad y exactitud de la respuesta, pero parece realmente exhaustiva y fue fácil de seguir. ¡Muchas gracias! Si desea mejorarlo, un breve resumen al principio puede ser beneficioso ya que es un texto bastante largo y los puntos principales podrían visualizarse fácilmente como una lista anidada o un diagrama de árbol para las personas que conocen términos como "interno / externo". enlace "
gebbissimo
18

En realidad es bastante simple. Si declara una variable como estática en el alcance de una función, su valor se conserva entre llamadas sucesivas a esa función. Entonces:

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

se mostrará en 678lugar de 666, porque recuerda el valor incrementado.

En cuanto a los miembros estáticos, conservan su valor en todas las instancias de la clase. Entonces el siguiente código:

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

imprimirá 4, porque first.a y second.a son esencialmente la misma variable. En cuanto a la inicialización, vea esta pregunta.

Maciej Stachowski
fuente
Esto no aborda las variables de alcance del espacio de nombres.
Michael Hagar
10

Cuando declara una staticvariable en el ámbito del archivo, esa variable solo está disponible en ese archivo en particular (técnicamente, la unidad de traducción *, pero no lo compliquemos demasiado). Por ejemplo:

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;
}

Para una variable local , staticsignifica que la variable se inicializará en cero y retendrá su valor entre llamadas:

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;
}

Para las variables de clase , significa que solo hay una única instancia de esa variable que se comparte entre todos los miembros de esa clase. Dependiendo de los permisos, se puede acceder a la variable desde fuera de la clase utilizando su nombre completo.

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;
};

Marcar una función que no es de clase statichace que la función solo sea accesible desde ese archivo e inaccesible desde otros archivos.

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;
}

Para las funciones de miembros de la clase, marcarlas como staticsignifica que no es necesario invocar la función en una instancia particular de un objeto (es decir, no tiene un thispuntero).

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;
}
Nik Bougalis
fuente
8

Las variables estáticas se comparten entre cada instancia de una clase, en lugar de que cada clase tenga su propia variable.

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

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

MyClass classA;
MyClass classB;

Cada instancia de 'MyClass' tiene su propio 'myVar', pero comparte el mismo 'myStaticVar'. De hecho, ni siquiera necesita una instancia de MyClass para acceder a 'myStaticVar', y puede acceder a ella fuera de la clase de esta manera:

MyClass::myStaticVar //Assuming it's publicly accessible.

Cuando se usa dentro de una función como una variable local (y no como una variable miembro de la clase), la palabra clave estática hace algo diferente. Le permite crear una variable persistente, sin dar alcance global.

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;
}

Es una variable global en términos de persistencia ... pero sin ser global en alcance / accesibilidad.

También puede tener funciones miembro estáticas. Las funciones estáticas son básicamente funciones no miembros, pero dentro del espacio de nombres del nombre de la clase y con acceso privado a los miembros de la clase.

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;
}

Cuando llama a una función miembro, hay un parámetro oculto llamado 'esto', que es un puntero a la instancia de la clase que llama a la función. Las funciones miembro estáticas no tienen ese parámetro oculto ... son invocables sin una instancia de clase, pero tampoco pueden acceder a las variables miembro no estáticas de una clase, porque no tienen un puntero 'this' para trabajar. No se los llama en ninguna instancia de clase específica.

Jamin Gray
fuente
1
"Suponiendo que sea de acceso público". - no es.
Luchian Grigore
2
myStaticVarnecesita ser definido también. Es importante mencionar que al responder una pregunta sobre la semántica de la staticpalabra clave, ¿no le parece?
Pretoriano
@Praetorian: Gracias, arreglado.
Jamin Gray
1
@JaminGrey Por "estático autónomo" me refería a funciones estáticas que no son miembros, y las escribo siempre que necesito alguna funcionalidad nueva solo en el archivo CPP actual, y no deseo que el enlazador tenga que procesar un símbolo adicional.
VR
1
@VR Odd! Nunca supe que existía la funcionalidad . ¡Gracias por ampliar mi conocimiento!
Jamin Gray
1

No soy un programador en C, así que no puedo darle información sobre los usos de la estática en un programa en C correctamente, pero cuando se trata de la programación orientada a objetos, la estática básicamente declara que una variable, una función o una clase son iguales a lo largo de la vida del programa. Toma por ejemplo.

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

Cuando creas una instancia de esta clase en tu Main, haces algo como esto.

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

Estas dos instancias de clase son completamente diferentes entre sí y operan independientemente una de la otra. Pero si fueras a recrear la clase A así.

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

Volvamos a la página principal nuevamente.

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

Entonces a1 y a2 compartirían la misma copia de int x por lo que cualquier operación en x en a1 influiría directamente en las operaciones de x en a2. Entonces si tuviera que hacer esto

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
}

Ambas instancias de la clase A comparten variables y funciones estáticas. Espero que esto responda a su pregunta. Mi conocimiento limitado de C me permite decir que definir una función o variable como estática significa que solo es visible para el archivo en el que la función o variable está definida como estática. Pero esto sería mejor respondido por un chico de C y no por mí. C ++ permite las formas C y C ++ de declarar sus variables como estáticas porque es completamente compatible con C.

David Tr
fuente
1

¿Qué significa con la variable local? ¿Es esa una variable local de función?

Sí: no global, como una variable local de función.

Porque también hay que cuando declaras una función local como estática que solo se inicializa una vez, la primera vez que ingresa a esta función.

Correcto.

También solo habla sobre la duración del almacenamiento con respecto a los miembros de la clase, ¿qué pasa si no es específico de la instancia, eso también es una propiedad del no estático? ¿O es la duración del almacenamiento?

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

es decir, todas las instancias de Rla cuota int R::a- int R::aNunca se copian.

Ahora, ¿qué pasa con el caso con alcance estático y de archivo?

Efectivamente, un global que tiene constructor / destructor donde sea apropiado: la inicialización no se difiere hasta el acceso.

¿Cómo se relaciona la estática con el enlace de una variable?

Para una función local, es externa. Acceso: es accesible para la función (a menos que, por supuesto, la devuelva).

Para una clase, es externa. Acceso: se aplican los especificadores de acceso estándar (público, protegido, privado).

static También puede especificar la vinculación interna, dependiendo de dónde se declare (archivo / espacio de nombres).

Toda esta palabra clave estática es francamente confusa.

Tiene demasiados propósitos en C ++.

¿Alguien puede aclarar los diferentes usos del inglés y también decirme cuándo inicializar un miembro de clase estático?

Se inicializa automáticamente antes mainsi está cargado y tiene un constructor. Puede parecer algo bueno, pero el orden de inicialización está en gran medida fuera de su control, por lo que la inicialización compleja se vuelve muy difícil de mantener, y desea minimizar esto: si debe tener una estática, entonces las escalas locales funcionan mucho mejor en bibliotecas y proyectos En cuanto a los datos con una duración de almacenamiento estático, debe intentar minimizar este diseño, especialmente si es mutable (variables globales). El 'tiempo' de inicialización también varía por varias razones: el cargador y el kernel tienen algunos trucos para minimizar las huellas de memoria y diferir la inicialización, dependiendo de los datos en cuestión.

justin
fuente
1

Objeto estático: podemos definir miembros de clase estáticos usando palabras clave estáticas. Cuando declaramos un miembro de una clase como estático, significa que no importa cuántos objetos de la clase se creen, solo hay una copia del miembro estático.

Un miembro estático es compartido por todos los objetos de la clase. Todos los datos estáticos se inicializan a cero cuando se crea el primer objeto, si no hay otra inicialización presente. No podemos ponerlo en la definición de clase, pero se puede inicializar fuera de la clase como se hace en el siguiente ejemplo redeclarando la variable estática, utilizando el operador de resolución de alcance :: para identificar a qué clase pertenece.

Probemos con el siguiente ejemplo para comprender el concepto de miembros de datos estáticos:

#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;
}

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

Constructor called.
Constructor called.
Total objects: 2

Miembros de funciones estáticas: Al declarar un miembro de función como estático, lo hace independiente de cualquier objeto particular de la clase. Se puede llamar a una función miembro estática incluso si no existen objetos de la clase y se accede a las funciones estáticas utilizando solo el nombre de la clase y el operador de resolución de alcance ::.

Una función miembro estática solo puede acceder al miembro de datos estáticos, otras funciones miembro estáticas y cualquier otra función desde fuera de la clase.

Las funciones miembro estáticas tienen un alcance de clase y no tienen acceso al puntero de esta clase. Puede usar una función miembro estática para determinar si se han creado o no algunos objetos de la clase.

Probemos con el siguiente ejemplo para comprender el concepto de miembros de funciones estáticas:

#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;
}

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2
Nima Soroush
fuente
1
Sería correcto mencionar que estos paradigmas fueron tomados de tutorialspoint.com/cplusplus/cpp_static_members.htm
BugShotGG