Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Global Variable als Singleton-Ersatz

31 views
Skip to first unread message

Curtis Newton

unread,
Mar 29, 2012, 10:03:33 AM3/29/12
to
Hallo,

Grundproblem: ich will einen Zeitstempel. Unter Windows bekommt man den
mittels QueryPerformanceCounter. Das ist der aktuelle Wert des Counters.
Seine Frequenz (die sich niemals nach einem Systemstart ändert) erhält
man mittels QueryPerformanceFrequency.

QueryPerformanceFrequency will man ja nur einmal aufrufen und sich das
Ergebnis speichern. Als erstes dachte ich, nimm ein Singleton, was sich
bei der Erstellung (erstmaliger Aufruf von getInstance()) den Wert holt.

Dann kam ich drauf, dass doch globale Variable einmal beim Programmstart
(vor Aufruf von main()) initialisiert werden. Oder? Damit kann ich das
doch dann so vereinfachen (alles aus der cpp-Datei):

class PCFreq
{
LARGE_INTEGER _freq;
public:
PCFreq()
{
//Frequenz holen
QueryPerformanceFrequency(&_freq);
}

LARGE_INTEGER freq()
{
return _freq;
}
};

PCFreq frequency;

double get_timestamp()
{
LARGE_INTEGER count;
if(FALSE==QueryPerformanceCounter(&count))
{
return -1;
}

if(!frequency.freq().QuadPart)
{
return -1;
}

double PCFreq = double(frequency.freq().QuadPart)/1000.0;

return double(count.QuadPart)/PCFreq;
}

Oder? Oder noch einfacher und "geschlossener":

double get_timestamp()
{
class PCFreq
{
LARGE_INTEGER _freq;
public:
PCFreq()
{
//Frequenz holen
QueryPerformanceFrequency(&_freq);
}

LARGE_INTEGER freq()
{
return _freq;
}
};

static PCFreq frequency;

LARGE_INTEGER count;
if(FALSE==QueryPerformanceCounter(&count))
{
return -1;
}

if(!frequency.freq().QuadPart)
{
return -1;
}

double PCFreq = double(frequency.freq().QuadPart)/1000.0;

return double(count.QuadPart)/PCFreq;
}

Ist das so auch bei mehreren Prozessoren okay bzw. vom Standard
gesichert?

C.

M J

unread,
Mar 31, 2012, 2:21:38 AM3/31/12
to
Ja, ist seit C++11 vom Standard abgedeckt. Bei der zweiten Variante
fügt der Compiler ein Flag ein, das darüber Buch führt, ob die
Initialisierung schon stattgefunden hat. Dieses Flag ist gegen
konkurrierenden Zugriff geschützt.

Curtis Newton

unread,
Apr 2, 2012, 7:10:07 AM4/2/12
to
Und wie ist es vorher? Weiß da der Compiler, ob die Initialisierung
stattgefunden hat, die Initailisierung selber ist aber nicht geschützt?

C.

Björn

unread,
Apr 3, 2012, 10:40:41 AM4/3/12
to
C++03 kannte keine Threads und ging daher davon aus, dass alle Abläufe
grundsätzlich eine feste Reihenfolge haben. Bei Benutzung einer
Threadbibliothek musst du unter C++03 also durch selbst gesetzte Mutexe o.ä.
für eine geschützte Initialisierung sorgen.

Sichergestellt war und ist allerdings, dass globale statische Objekte
initialisiert werden, bevor die main-Funktion aufgerufen wird.

Noch zwei kleiner Verbesserungstipps:

1.) Wenn du deine Elementfunktion

LARGE_INTEGER freq()

umbenennst in einen Typumwandlungsoperator

operator LARGE_INTEGER ()

brauchst du sie nicht mehr explizit aufzurufen. Stattdessen ruft der
Compiler automatisch den Umwandlungsoperator auf, wenn er einen
LARGE_INTEGER braucht.

2.) Noch bequemer ist es, wenn du frequency gleich als LARGE_INTEGER
deklarierst und deine Klasse PCFreq im Konstruktor deren Initialisierung
durchführt. Dann musst du lediglich einmalig eine Instanz von PCFreq
anlegen, die du sofort wieder wegschmeißen kannst.

- Björn

M J

unread,
Apr 3, 2012, 12:05:10 AM4/3/12
to
On 2 Apr., 08:10, Curtis Newton <cnew...@web.de> wrote:
Nachtrag 2: Befinden sich die genannten Variablen in
unterschiedlichen Übersetzungseinheiten, dann gibt es
keine vom Standard garantierte Möglichkeit, die
Initialisierungsreihenfolge festzulegen. Dann bliebe
also das Singleton.

M J

unread,
Apr 2, 2012, 11:36:28 PM4/2/12
to
On 2 Apr., 08:10, Curtis Newton <cnew...@web.de> wrote:
Nachtrag: Zu beachten sind Fälle, in denen außerhalb einer Funktion
definierte statische Variablen während ihrer Initialisierung auf
andere
außerhalb einer Funktion definierte statische Variablen zugreifen.
Falls
das unübersichtlich wird, bist du mit der von dir angegebenen
zweiten Variante, die übrigens dem Singleton-Muster
entspricht, auf der sicheren Seite.

M J

unread,
Apr 2, 2012, 10:35:13 PM4/2/12
to
On 2 Apr., 08:10, Curtis Newton <cnew...@web.de> wrote:
Meinst du mit "vorher" dein erstes Beispiel? Da besteht keine
Notwendigkeit, über die Initialisierung Buch zu führen,
da statische Variablen außerhalb von Funktionen beim
Programmstart initialisiert werden, wie du richtig schreibst.
Folglich muss die Initialisierung in dem Fall nicht geschützt
werden.

M J

unread,
Apr 3, 2012, 3:59:05 PM4/3/12
to
On 3 Apr., 11:40, Björn <he...@nurfuerspam.de> wrote:
> Curtis Newton wrote:
> > On 2012-03-31, M J <m.jaro...@gmail.com> wrote:
> >> On 29 Mrz., 11:03, Curtis Newton <cnew...@web.de> wrote:
>
[...]
>
> Sichergestellt war und ist allerdings, dass globale statische Objekte
> initialisiert werden, bevor die main-Funktion aufgerufen wird.

Nein, ist es nicht; das hängt von der Implementierung ab.
Sichergestellt ist nur, dass die Initialisierung vor der ersten
Verwendung stattfindet.

Thomas Richter

unread,
Apr 5, 2012, 3:34:27 PM4/5/12
to
In der Tat ist es das, was die g++-Implementierung macht: Der Compiler
erzeugt einen ode, der etwa dem folgenden entspricht:

mutex_lock(&local_mutex);
if (!initialized) {
init_static_variable;
initialized = true;
}
mutex_unlock(&local_mutex);

Hat leider den Nachteil, das bei jedem Durchlauf einer Funktion mit
einer statischen Variable ein gewisser Overhead erzeugt wird.

Grüße,
Thomas

M J

unread,
Apr 6, 2012, 3:58:53 PM4/6/12
to
Und das macht der g++ auch bei nicht-lokalen statischen Variablen?
(Denn das war im Moment das Thema.)

Stefan Reuther

unread,
Apr 7, 2012, 11:33:51 AM4/7/12
to
Thomas Richter wrote:
> On 03.04.2012 21:59, M J wrote:
>> Nein, ist es nicht; das hängt von der Implementierung ab.
>> Sichergestellt ist nur, dass die Initialisierung vor der ersten
>> Verwendung stattfindet.

Nur dann, wenn die Initialisierung nach main verzögert wird. Vor dem
Eintritt in main ist nicht sichergestellt, dass ein Objekt vor seiner
Benutzung initialisiert wird.

> In der Tat ist es das, was die g++-Implementierung macht: Der Compiler
> erzeugt einen ode, der etwa dem folgenden entspricht:
>
> mutex_lock(&local_mutex);
> if (!initialized) {
> init_static_variable;
> initialized = true;
> }
> mutex_unlock(&local_mutex);

Wobei das definitiv nicht alle (C++98-)Compiler tun. Es empfiehlt sich
also, da selbst noch nachzuhelfen.


Stefan

M J

unread,
Apr 8, 2012, 12:34:54 PM4/8/12
to
On 7 abr, 12:33, Stefan Reuther <stefan.n...@arcor.de> wrote:
> Thomas Richter wrote:
> > On 03.04.2012 21:59, M J wrote:
> >> Nein, ist es nicht; das hängt von der Implementierung ab.
> >> Sichergestellt ist nur, dass die Initialisierung vor der ersten
> >> Verwendung stattfindet.
>
> Nur dann, wenn die Initialisierung nach main verzögert wird. Vor dem
> Eintritt in main ist nicht sichergestellt, dass ein Objekt vor seiner
> Benutzung initialisiert wird.

Ja. War auch so gemeint. (Siehe meine anderen Beiträge.)

> > In der Tat ist es das, was die g++-Implementierung macht: Der Compiler
> > erzeugt einen ode, der etwa dem folgenden entspricht:
>
> > mutex_lock(&local_mutex);
> > if (!initialized) {
> >     init_static_variable;
> >     initialized = true;
> > }
> > mutex_unlock(&local_mutex);
>
> Wobei das definitiv nicht alle (C++98-)Compiler tun. Es empfiehlt sich
> also, da selbst noch nachzuhelfen.

Beim "selbst nachhelfen" aber besser nicht wie oben angegeben, da
nicht Exception-fest.
0 new messages