Dzisiaj będzie o tym, jak napisać funkcję, która automatycznie wykona się jeszcze zanim wystartuje funkcja main. Fajna sztuczka z dużym powerem. Zaczynamy!
Przykład
Do tego, aby funkcja odpaliła się na samym starcie aplikacji, wykorzystamy fakt, że zmienne globalne są inicjalizowane na samym początku, jeszcze przed uruchomieniem funkcji main :
#include <iostream> using namespace std; int initialize() { cout << "aaa...jestem pierwsza!" << endl; } int dummy = initialize(); int main() { cout << "funkcja main" << endl; return 0; }
Program po odpaleniu wyświetli nam:
> aaa...jestem pierwsza! > funkcja main
Czyli nasza funkcja startuje wcześniej niż main :)
Wytłumaczenie
Globalna zmienna dummy ma tutaj tylko jedno zadanie – stać się przyczyną uruchomienia funkcji initialize.
Ponieważ zmienne globalne zawsze są inicjalizowane przed uruchomieniem funkcji main, to musi też zostać zainicjalizowana nasza zmienna dummy. W tym celu zostanie wywołana nasza funkcja initialize, i… voila!
Zastosowanie
Przyjmijmy, że chcemy sobie napisać klasę Random, generującą liczby pseudolosowe w przedziale 0..1. Najwygodniej będzie chyba, jeśli zrobimy z niej klasę statyczną – w ten sposób będzie można wywoływać jej metody bez tworzenia obiektu. Który nie jest nam do niczego potrzebny. Pamiętasz pewnie, że generator liczb pseudolosowych musi zostać zainicjalizowany, jednokrotnie, przed pierwszym użyciem funkcji rand? Do tego właśnie celu wykorzystamy naszą samostartującą funkcję:
#include <cstdlib> #include <ctime> class Random { private: static int dummy; static int initialize() { srand(time(0)); return 0; } public: static double getDouble() { return (double) rand() / RAND_MAX; } }; int Random::dummy = initialize();
Inicjalizacja składowej statycznej dummy wymusza uruchomienie prywatnej metody initialize, która z kolei inicjalizuje generator liczb pseudolosowych. W ten sposób nie musimy sami bawić się w inicjalizowanie generatora liczb – sprytny trick sprawi, że generator będzie zawsze zainicjalizowany w momencie startu aplikacji!
Wady
Wada jaka mi się nasuwa jest taka, że jeśli napiszemy drugą taką sprytną klasę z automatycznym inicjalizowaniem, to nie będziemy wiedzieć, który inicjalizator wystartuje pierwszy – brak gwarancji, że nasz Random będzie zainicjalizowany w chwili użycia.
Jeśli jakieś inne wady/zalety tej konstrukcji przychodzą Ci do głowy, a tym bardziej, jakieś ciekawe zastosowania – podziel się nimi w komentarzu!
March 30th, 2011 on 21:39
Wprawdzie nie ma to nic wspólnego z C++, ale na platformie Windows możesz uruchomić jeszcze wcześniej dowolną funkcję zaraz przed tym jak system skacze do punktu wejściowego aplikacji (entrypoint), wykorzystując mechanizm TLS Callbacks, które są “trochę” podobnie zaprojektowane jak punkty wejściowe bibliotek DLL, niewiele aplikacji z tego korzysta (np. manager plików FAR) i jeszcze mniej ludzi o tym wie, przykładowe opisy:
http://www.hexblog.com/?p=9
http://www.codeproject.com/KB/threads/tls.aspx
pozdrawiam
March 31st, 2011 on 08:31
@Bartosz Wójcik
rzeczywiście, pierwszy się z tym spotykam… dobrze wiedzieć, że taki mechanizm istnieje. Dzięki!
April 28th, 2011 on 19:25
>int dummy = initialize();
zbytnio nie zabangla
July 24th, 2011 on 15:41
Ano jednak zabangla… wystarczy sprawdzić… (sprawdziłem ;))