Autor Wątek: Przekazywanie zmiennej globalnej do funkcji  (Przeczytany 3759 razy)

Offline slowbro

  • Użytkownik

# Sierpień 06, 2016, 22:41:01
Cześć

Mam w swoim programie zmienne globalne dotyczące czasu:

DWORD g_CurrTime = 0;            
DWORD g_LastTime = 0;            
DWORD g_Time1 = 0, g_Time2 = 0;   
GLfloat g_DeltaTime = 0;

oraz funkcje, które wykorzystują te zmienne. Zasadne jest jawne przekazywanie zmiennej globalnej do funkcji z waszego doświadczenie skoro i tak zmienne globalne są widoczne i dostępne w całym programie np. void DrawScene (DWORD g_CurrTime) czy porostu lepiej jest nie przekazywać jej do funkcji void DrawScene () tylko poprostu wykorzystć zmienną globalną w kodzie funkcji?

Pozdrawiam


Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Kyroaku

  • Użytkownik

  • +1
# Sierpień 06, 2016, 23:10:41
Zacznijmy od tego, że nie jest zasadne tworzenie zmiennych globalnych ;) (na pewno nie w tym przypadku)
I nie, nie jest zasadne przekazywanie tego jako argumentu. Wydłużasz sobie tylko wywołanie funkcji c(:

Offline MTP

  • Użytkownik

# Sierpień 07, 2016, 02:37:54
Skoro to C++, stwórz klasę - będzie przejrzyściej. Jeśli w innych miejscach programu potrzebujesz tych zmiennych to przekaż te obiekty, które korzystają z czasu do instancji Game.
Polecam poczytać o Dependency Inversion i wzorcach projektowych.

Pseudokod:

class Game {
    DWORD currTime;
    DWORD lastTime;
    DWORD Time1, Time2;
    GLfloat deltaTime;

    std::vector<Hero> heroes;

    void drawScene() {
    /* kod */
    }
    void addHero(Hero hero) {
        heroes.push_back(hero);
    }
    void updateHeroes() {
        for (uint  i = 0; i < heroes.size(); ++i ){
            hero.update(deltaTime);
    }
};

class Hero {
    void Update(GLfloat delta) {
        // aktualizacja bohatera
    }
};

// gdzieś indziej podczas inicjalizacji
Game game = new Game();
game->addHero(new Hero());

// w pętli gry
game->updateHeroes();
game->drawScene();

Offline Kyroaku

  • Użytkownik

# Sierpień 07, 2016, 19:05:42
Ja bym też te zmienne od czasu zamknął w klasie.

Offline hashedone

  • Użytkownik

  • +1
# Sierpień 08, 2016, 22:07:30
Wydłużasz sobie tylko wywołanie funkcji c(:
Znaczy że trzeba napisać więcej literek, czy że przeraża Cię dodatkowy "push" (a tak na prawdę nie "push" tylko "mov")? Zapewniam Cię że ilość argumentów do przekazania (przynajmniej typu tak prostego jak int - inaczej może być, jeśli konstruktor kopiujący jest nietrywialny, lub ilość enkapsulowanych danych jest bardzo duża) nie ma żadnego (a już na pewno zauważalnego) wpływu na wydajność wykonania. W szczególności w tak prostym przypadku argument prawie na pewno i tak zostanie przekazany przez rejestr, a kompilator zadba, żeby bezpośrednio w wyniku poprzednich obliczeń tam się ten argument znalazł.

W tym przypadku stanowczo najlepszym rozwiązaniem jest przekazanie delty w formie argumentu i zupełnie zrezygnowanie ze zmiennych globalnych. Dodanie klasy może mieć sens, jeśli potrzeba abstrakcji która ma stan i operacje które trzeba na nim wykonać (w szczególności: delta czasu nie jest stanem i nie ma specjalnie sensu trzymanie jej jako członka klasy). I jasne - w tak prostym przypadku to to kwestie "religijne", natomiast jak tylko zacznie się trochę więcej w Twoim kodzie dziać, zaczniesz mieć problemy z jego utrzymaniem, bo API będzie miało niejasne zależności.

Offline Kyroaku

  • Użytkownik

  • +1
# Sierpień 09, 2016, 20:49:29
Cytuj
Znaczy że trzeba napisać więcej literek, czy że przeraża Cię dodatkowy "push"
Znaczy, że chodzi mi o wygląd i wygodę kodu.

Ja bym przekazał deltę, jako argument, ale też nie tworzyłbym zmiennych globalnych. Jeśli już są i autor nie da się przekonać do ich usunięcia, to nie przekazywałbym, jako argument.

W klasie bym zamknął te zmienne, ponieważ jeśli autor chce używać nie tylko delty, ale i reszty (w pierwszym poście przekazuje CurrTime), to lepiej przekazać jeden obiekt klasy, niż 2 zmienne intowe.

Oczywiście to wszystko jest pod nagłówkiem "IMHO". Niech każdy kodzi jak chce.

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Sierpień 10, 2016, 12:03:18
W tym przypadku stanowczo najlepszym rozwiązaniem jest przekazanie delty w formie argumentu i zupełnie zrezygnowanie ze zmiennych globalnych.
Też jestem zwolennikiem takiego rozwiązania. Dzięki temu możemy podać gdze deltaTime=0, wtedy WSZYSTKO w grze się zatrzymuje i mamy "za darmo" pauzę idealną. :) Tylko ważne żeby gra gdzieś w środku jakoś "na lewo" nie pobierała sobie jakiegoś currentTimw tylko zawsze korzystała z deltaTime.

Offline laggyluk

  • Użytkownik
    • http://laggyluk.com

# Sierpień 10, 2016, 12:41:52
Też jestem zwolennikiem takiego rozwiązania. Dzięki temu możemy podać gdze deltaTime=0, wtedy WSZYSTKO w grze się zatrzymuje i mamy "za darmo" pauzę idealną. :) Tylko ważne żeby gra gdzieś w środku jakoś "na lewo" nie pobierała sobie jakiegoś currentTimw tylko zawsze korzystała z deltaTime.
mając jedną globalną deltę też można ją ustawić na zero :P jeżeli już są te zmienne globalne to nie ma sensu tego przerabiać dla 'większej estetyki' w jednoosobowym projekcie. Za to przekazywanie takiej zmiennej jako argumetn mija się z celem i zaciemnia kod imo.

Offline MrKaktus

  • Użytkownik

# Sierpień 10, 2016, 13:58:37
Za to przekazywanie takiej zmiennej jako argumetn mija się z celem i zaciemnia kod imo.
Wrecz przeciwnie. W ten sposob majac stos stanow gry mozna w prosty sposob zrobic chociazby :
- pauza swiata gry (3d) renderowanego wciaz w tle (dT = 0)
- podczas gdy z przodu mamy animowane menu ( dT )
LUB
powyzszy case, ale dodatkowo foliage w grze wciaz sie ladnie animuje, wiatr wieje, a cala reszta stoi w miejscu.
dT przekazuje sie kaskadowo decydujac co ma byc spalzowane a co nie.

Offline laggyluk

  • Użytkownik
    • http://laggyluk.com

# Sierpień 10, 2016, 14:14:29
to się nie wyklucza, unity przykładowo ma globalny Time.deltaTime i Time.timeScale którym można zapauzować różne rzeczy ale nie wpływa to na UI z tego co pamiętam

Offline izaw

  • Użytkownik

# Sierpień 10, 2016, 22:05:21
Przekazując zmienną masz wartość w funkcji z chwili wykonania. Globalna zawsze da aktualną. Nieraz zmienne globalne są zmieniane w wielu miejscach, niestety często nizamierzenie co powoduje trudne do wykrycia błędy.

Offline Kyroaku

  • Użytkownik

# Sierpień 10, 2016, 23:33:32
Cytuj
Nieraz zmienne globalne są zmieniane w wielu miejscach, niestety często nizamierzenie co powoduje trudne do wykrycia błędy.
Zmienna jest zmieniana niezamierzanie ? Mógłbyś wyjaśnić ?

Offline Xion

  • Moderator
    • xion.log

  • +3
# Sierpień 11, 2016, 09:43:05

Offline koirat

  • Użytkownik

# Sierpień 11, 2016, 10:42:06
Przekazywanie przez argument ma ten plus że można robić kaskadę updatów gdzie każdy ojciec może modyfikować delta time dla swojego potomka.

Ogólnie przekazywał bym przez argument - daje to wiele możliwości.

Offline hashedone

  • Użytkownik

  • +1
# Sierpień 11, 2016, 19:02:37
Ogólnie post Xiona zamyka temat zmiennych globalnych. Jeśli ktoś po nim dalej uważa, że mogą mieć zastosowanie tu, bądź gdziekolwiek, niech używa ich dalej i jak trafi mu się któryś z problemów tam opisanych, niech przestanie ich używać zamiast szukać obejść na problemy które sam produkuje.

Jest ewentualnie kwestia zamykania takich danych w klasie w której jest wykorzystywana - prawie zawsze jest to złe, bo prawie zawsze ta zmienna jest wyliczana dla danego momentu w jednym miejscu, w danym momencie czasu i dalej na jej podstawie przeliczany jest faktyczny stan aplikacji (tu: gry). Nie jest to natomiast część trwałego stanu obiektu - dt jest poprawny tylko dla danego kwantu czasu (co w przypadku architektury oprogramowania prawie zawsze odpowiada konkretnemu stanowi stosu od danego miejsca w dół). Jeśli natomiast z jakiegoś powodu potrzebne jest więcej niż jedna zmienna przekazywana równolegle z jakimiś innymi zmiennymi, można je jak najbardziej zawrzeć w jakieś innej strukturze, która jest przekazywana jako całość. I tak, ja wiem, że w C++ klasy i struktury to językowo to samo, jednak kluczowe jest tu to, że nie jest to ta sama klasa, w której te dane są wykorzystywane. Pomijam fakt, że nie spotkałem się z sytuacją, kiedy aktualny czas, poprzedni czas, czy jakiekolwiek dodatkowe czasy reprezentowane u Ciebie przez g_Time1 i g_Time2 były by potrzebne wszędzie tam gdzie się korzysta z delty czasu - prawie zawsze, używa się  tych danych tylko w jednym miejscu do wyliczenia delty, a dalej operuje właśnie na delcie.

Zaznaczam, że wszędzie, gdzie napisałem "prawie zawsze", świerzbiło mnie, żeby napisać "zawsze", bo nie potrafię wyobrazić sobie sensownej sytuacji, kiedy zachodziła by inna sytuacja, jakkolwiek dopuszczam taką możliwość - jeśli bym jednak na taką sytuację trafił, co najmniej kilkakrotnie zastanowiłbym się, czy nie mam gdzieś  błędu w logice/architekturze aplikacji.