Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: digimikeh en 29 Agosto 2019, 05:16 am



Título: Variables estaticas...
Publicado por: digimikeh en 29 Agosto 2019, 05:16 am
Muy buenas..

Tengo un mal concepto quizá de lo que son las variables estáticas, yo las imagino algo asi como un contenedor que se crea automáticamente al iniciarse un programa, vive durante todo el programa y luego se cierra una vez que el programa termina... como un bolso de viaje que tomas al iniciar el viaje y te deshaces de él cuando regresas a casa... asi es como yo las veo.. (por favor, indicarme si estoy mal).

El siguiente código no esta reflejando esto:

main.cpp
Código
  1. #include <iostream>
  2. #include "Archivo2.h"
  3. #include "Archivo1.h"
  4.  
  5. int main() {
  6. x::num = 10;
  7. std::cout << "Desde main -> " << x::num << "Memoria : " << &x::num;
  8. std::cout << std::endl;
  9. Archivo1 a;
  10. a.printVal();
  11. return 0;
  12. }
  13.  


Archivo1.h
Código
  1. #pragma once
  2. struct Archivo1{
  3. void printVal() const;
  4. };
  5.  

Archivo1.cpp
Código
  1. #include "Archivo2.h"
  2. #include "Archivo1.h"
  3. #include <iostream>
  4.  
  5. void Archivo1::printVal() const {
  6. std::cout << "Desde Archivo1 -> " << x::num << "Memoria : " << &x::num;
  7.  
  8. }
  9.  

Archivo2.h
Código
  1. namespace x{
  2.     static int num;
  3. }
  4.  


La salida de consola "Desde Main ->" imprime 10
La salida de consola "Desde Archivo1 ->" imprime 0...

Creí que estaba imprimiendo el mismo elemento, pero no, cuando le agregue que imprimiera la dirección de memoria, me encontré con la sorpresa de que ambas estan imprimiendo direcciones diferentes....

En qué momento se creó una nueva instancia de la variable estática y como puedo solucionar este dilema?.. mi intención es escribir e imprimir en una sola variable x::num

Saludos_!



Título: Re: Variables estaticas...
Publicado por: @XSStringManolo en 29 Agosto 2019, 10:15 am
Prueba añadiendo external antes de static int num; en el archivo2.h

Parece un problema de linkado, que te crea otra static nueva en el .cpp.


Título: Re: Variables estaticas...
Publicado por: Loretz en 29 Agosto 2019, 15:04 pm
El contenido de los archivos header (*.h) se copia y pega en cada lugar donde el compilador encuentra su #include. En tu caso se está escribiendo
Código:
namespace x {
    inline static int num;
}
en cada lugar donde se encuentre
Código:
#include "Archivo2.h"

Así, tendrás una x::num en cada cpp, una inicializada a 10 y la otra a 0 ("zero initialized", como exige el estándar).

Pero, además, las variables static tiene un alcance ("scope") a nivel de unidad de traducción (básicamente cada cosa que vaya a compilarse como *.obj), así que es natural que tengas una nueva x::num en cada cpp, esa es su naturaleza. Si quieres que sea la misma, no debería ser static.


Título: Re: Variables estaticas...
Publicado por: digimikeh en 29 Agosto 2019, 17:11 pm
Gracias por las respuestas...


Manolo, te refieres a extern ?

Loretz, entonces las variables estáticas se instancian mediante la inclusión de archivos y no de tipos ... es asi?... por ejemplo, lo que has dicho: estoy incluyendo el mismo archivo en dos archivos independientes, serian dos instancias separadas de la variable estatica... sin embargo, si instancio dos tipos Archivo2 en un mismo archivo, ahi si estaria utilizando la misma variable estatica para ambos casos.... ?

Suponiendo que tengo una clase llamada Alpha, que tiene un campo publico estático llamado myName, si yo instancio esta clase mas de una vez dentro del mismo archivo fuente, todas las instancias de la clase estarían accediendo a la misma dirección de memoria de myName...

Es al menos lo que veo aqui en este ejemplo:
https://www.tutorialspoint.com/cplusplus/cpp_static_members.htm (https://www.tutorialspoint.com/cplusplus/cpp_static_members.htm)

Saludos..


Título: Re: Variables estaticas...
Publicado por: Loretz en 29 Agosto 2019, 21:08 pm
Citar
Loretz, entonces las variables estáticas se instancian mediante la inclusión de archivos y no de tipos ... es asi?...
No no. Lo que quise decir es que cualquier cosa que escribas en un archivo.h se va a "pegar" en el archivo.cpp justo ahí donde hayas puesto "#include archivo.h". Es como si el compilador hiciera un "buscar y reemplazar", y ahí donde encuentre "#include archivo.h" lo va a reemplazar por el contenido de ese archivo. Sea lo que sea que haya escrito en ese .h.

Citar
... sin embargo, si instancio dos tipos Archivo2 en un mismo archivo, ahi si estaria utilizando la misma variable estatica para ambos casos.... ?
Esta parte no la entiendo. Si a lo que te refieres es a si escribes dos veces el mismo #include en un mismo *.cpp, si has puesto la guarda "pragma once" (o las directivas #ifndef ... #define ... #endif) el compilador ignorará la segunda inclusión; y si no la has puesto, tendrás un error al compilar, porque estarías violando la regla "ODR" (one definition rule". (Puedes consultar la definición en https://en.cppreference.com/w/cpp/language/definition (https://en.cppreference.com/w/cpp/language/definition))

Citar
Suponiendo que tengo una clase llamada Alpha, que tiene un campo publico estático llamado myName ...
Aquí sí, es como dices y se muestra en el ejemplo que mencionas. Pero en este caso ya no sería una variable static sino un dato miembro declarado static, que es otra cosa. Siguen reglas distintas.


Título: Re: Variables estaticas...
Publicado por: digimikeh en 29 Agosto 2019, 22:16 pm
La cita 2 está relacionada con la 3... es que no me supe explicar correctamente...lo siento  :rolleyes:

Entonces la solución que tengo es lo que planteaba Manolo std::string con la palabra extern de lo contrario sería usar objetos persistentes pero no creo que esto sea muy elegante....




Título: Re: Variables estaticas...
Publicado por: Loretz en 29 Agosto 2019, 22:32 pm
Si lo que necesitas es una misma variable accesible desde distintos cpp, deberías declararla "extern", pero ya no sería posible que fuera "static", porque en este sentido son contradictorias.

Por ejemplo:
en Archivo1.h

Código:
#pragma once

struct Archivo1 {
    void printVal() const;
};

en Archivo2.h
Código:
#pragma once

namespace x {
    extern int num;  // no puede ser static
}

en Archivo1.cpp
Código:
#include "Archivo1.h"
#include "Archivo2.h"
#include <iostream>

void Archivo1::printVal() const {
    std::cout << "Desde Archivo1 -> " << x::num << " Memoria : " << &x::num << '\n';
}

en main.cpp
Código:
#include <iostream>
#include "Archivo2.h"
#include "Archivo1.h"

int x::num = 10; // definida aquí o en cualquier otro cpp

int main() {
    std::cout << "Desde main -> " << x::num << " Memoria : " << &x::num << '\n';
    Archivo1 a;
    a.printVal();
    return 0;
}

Salida:
Código:
Desde main -> 10 Memoria : 00FBC000  // mismo valor, misma dirección de memoria.
Desde Archivo1 -> 10 Memoria : 00FBC000

Al declararla como "extern" le dices al compilador que está definida en algún otro lado, y es conocida en todo el programa, en cualquier cpp donde se incluya su declaración (por medio de #include Archivo2.h).

Esto mismo también puede hacerse (a partir de C++17) con
Archivo2.h
Código:
#pragma once

namespace x {
    inline int num = 10;
}

Y ya no necesitas definir x::num en ninguno de los cpp.





Título: Re: Variables estaticas...
Publicado por: digimikeh en 30 Agosto 2019, 17:22 pm
De acuerdo, me parece lógico...

Bueno, en este caso he querido usar un int como ejemplo para describir mi duda, en el programa que estoy intentando hacer, lo que quiero usar como variable global es un tipo propio que se inicializa con un constructor.  En el constructor de ese tipo propio (que se llama Sesion) se inicializan los campos privados, por lo que ...

Código
  1. extern Sesion s;
  2.  

...estaría inicializando dichos campos automáticamente, aún asi debo volver a definir tal como lo hiciste cuando escribiste esta linea ? :

Código
  1. int x::num = 10; // definida aquí o en cualquier otro cpp
  2.  

O es que al escribir extern Sesion s en realidad no estoy ejecutando el constructor con su inicialización?

Saludos! y gracias por las respuestas... han sido de mucha ayuda @std::string Manolo y @Loretz.


Título: Re: Variables estaticas...
Publicado por: Loretz en 30 Agosto 2019, 19:31 pm
Para objetos de alguna clase es lo mismo que para tipos nativos, puedes usar las dos formas, declarar el objeto como extern o como inline (mejor).

Por ejemplo, usando extern podría ser algo así:
Archivo1.h
Código:
#pragma once
#include <string>

extern struct X {
    X(std::string str) : str(str) {}
    int num = 10;
    std::string str;
} x;

Y en algún cpp
Código:
X x{"hola"};

Usando inline no necesitas que la definición esté en un cpp
Código:
#pragma once
#include <string>

inline struct X {
    X(std::string str) : str(str) {}
    int num = 10;
    std::string str;
} x{"hola"};

Estos son un par de ejemplos de cómo podía hacerse antes y ahora con el estándar C++17, pero hay otras cosas a considerar, probablemente necesites un constructor por defecto, y asignarle valores a tu objeto cuando el programa los conozca, o crear el objeto en la memoria libre (usando un smart pointer, por supuesto), o ... Bueno, ya verás.

[EDITO] Ah, me olvidaba
Citar
O es que al escribir extern Sesion s en realidad no estoy ejecutando el constructor con su inicialización?
No, no estás ejecutando el constructor, porque extern Sesion es una declaración no una definición.




Título: Re: Variables estaticas...
Publicado por: digimikeh en 31 Agosto 2019, 23:07 pm
ok, entiendo, pero yo tenía entendido que cuando tu definías algo, estas usando el constructor... o es que extern es la excepción?

Alpha.h
Código
  1. class Alpha{
  2.     int x;
  3. public:
  4.     Alpha() : x{5}{}
  5.     //hacer otras cosas
  6. }
  7.  

main.cpp
Código
  1. #include "Alpha.h"
  2.  
  3. int main (){
  4.     Alpha a;            //Nota (a)
  5. }
  6.  

Nota (a) : A pesar de estar declarando un objeto llamado a de tipo Alpha se estaría inicializando el campo privado x y valdría 5... (es asi como yo tengo en mi programa el objeto Sesion inicializa sus campos privados a un string "NA".

Sin embargo, acabo de darme cuenta que incializar y definir no son la misma cosa...




Título: Re: Variables estaticas...
Publicado por: Loretz en 31 Agosto 2019, 23:53 pm
Citar
ok, entiendo, pero yo tenía entendido que cuando tu definías algo, estas usando el constructor... o es que extern es la excepción?
Exactamente, así es; extern es para indicar que se trata de una declaración, no una definición, por eso es que la definición debe ir en algún otro lado.

De todos modos hoy (a partir del C++17) es preferible la declaración inline https://en.cppreference.com/w/cpp/language/inline

Citar
Sin embargo, acabo de darme cuenta que incializar y definir no son la misma cosa...

... Más o menos... o, mejor, casi nunca...
Declarar, definir, inicializar, son palabras que tienen significados muy específicos, y con unos cuantos casos particulares, que algunas veces no ayudan para nada a la intuición.

En algunos casos la declaración y la definición son la misma cosa;
En casi todos los casos una definición implica la inicialización.

En matemática elemental tienes palabras como minuendo, sustraendo, resta, diferencia, que no pueden confundirse; o, también, dividendo, divisor, cociente, división. 0, ¿qué tal porciento, porcentaje, tasa, razón?  Cada una significa algo específico pero que en el habla común usamos sin mucho cuidado. Bueno, en C++ es mucho peor.



Título: Re: Variables estaticas...
Publicado por: digimikeh en 2 Septiembre 2019, 01:14 am
Muy bien ya me ha quedando claro...
 :o

Gracias..