Hallo,
ich habe sehr lange (fast 20 Jahre :-)) nicht mehr in c++ programmiert
und bin im Moment dabei zu wiederholen und aufzuholen, weil ich nächstes
Jahr ein größeres Projekt angehen möchte.
Folgendes Problem ist vermutlich trivial, erschließt sich mir aktuell
aber nicht. Je nachdem wo die Variable "static int64 mseconds" definiert
wird, funktioniert der Code oder auch nicht.
Funktionieren heißt, bei jedem Aufruf der class member function
"::buttonClicked" wird der Wert von mseconds aktualisiert. Nicht
funktionieren heißt, beim ersten Aufruf übernimmt die Variable einen
korrekten Wert, wird später aber nicht aktualisiert.
Der Code benutzt Library Funktionen, die sollten aber nicht stören, weil
ich vermute, dass das Problem darin liegt, dass mir die Bedeutung von
static nicht vollständig klar ist und nicht in Nebenwirkungen der
Library.
Ich würde das Problem gern verstehen, kann mir jemand einen
sachdienlichen Hinweis geben?
Der Code:
funktioniert nicht:
vlg
Timm
Edit: Jaja, man braucht hier kein static, das ist aber eine andere
Frage. Ich möchte in der nächsten Iteration gern die Zeit des letzten
Aufrufs wissen ...
Timm R. schrieb:> Ich würde das Problem gern verstehen, kann mir jemand einen> sachdienlichen Hinweis geben?http://en.cppreference.com/w/cpp/language/storage_duration
Und da einfach mal den Absatz über Static local variables lesen. In
Deinem "geht nicht"-Beispiel wird die Variable nur einmal mit dem
Funktionsaufruf initialisiert, im anderen Beispiel initialisiert (mit 0)
und dann immer wieder zugewiesen. Falsch (unangebracht) ist das `static`
in beiden Beispielen.
Hallo,
Carl D. schrieb:> Die statische Variable wird eben im ersten Beispiel nur einmal> initialisiert. Die lokale im zweiten jedesmal.
ach herrje, wie simpel. Ist ja eigentlich klar, wenns mans weiß :-)
Herzlichen Dank!
Timm
Für alle mitlesenden nur zur Sicherheit: Die static-Variable existiert
einmal für die ganze Klasse, nicht pro Objekt, wie man vielleicht
vermuten könnte. Wenn es also mehrere MainContentComponent-Objekte gibt,
sollte einem dies bewusst sein. Wenn man die static-Variable tief in
einer unübersichtlichen Funktion versteckt, kann man seinen Nachfolgern
lustige Fallen stellen:
1
#include<iostream>
2
#include<string>
3
4
classKnopf
5
{
6
std::stringname;
7
public:
8
voiddrueck(){
9
staticsize_taufrufe=0;
10
++aufrufe;
11
std::cout<<"Knopf "<<name<<" wurde "<<aufrufe<<" mal gedrückt.\n";
12
}
13
Knopf(constchar*s):name(s){
14
}
15
};
16
17
intmain()
18
{
19
Knopfein("Ein");
20
ein.drueck();
21
ein.drueck();
22
// Jahre spaeter nachgeruestet, der Geraet hat einen Ausschalter bekommen:
Danke für die Info.
Nicht das ich jemals auf die Idee gekommen wäre eine static Variable in
einer Methode zu definieren, aber das Ergebnis überrascht mich doch.
Hätte intuitiv auch erwartet, dass es die Variable für jedes Objekt
einmal gibt.
Wenn man aber noch mal genauer darüber nachdenkt, ist es recht
konsequent gelöst. Eine static variable wird genau dann das erste Mal
gesetzt, wenn der Code dazu das erste mal ausgeführt wird (jaja, es sei
denn sie ist 0, dann darf der Compiler das auch schon davor).
Man kann sich auch wenn man C++ fast täglich verwendet, immer wieder
überraschen lassen :D
nfet schrieb:> Man kann sich auch wenn man C++ fast täglich verwendet, immer wieder> überraschen lassen :D
In dem Fall des OP handelt es sich um Funktions lokale, statische
Variablen und die gibt es auch schon in C. Hat also überhaupt nichts mit
Klassen oder Objekten zu tun.
nfet schrieb:> Hätte intuitiv auch erwartet, dass es die Variable für jedes Objekt> einmal gibt.
Das kann nicht so sein, denn: Nachdem der Compiler die "class"
Deklaration einer Klasse eingelesen hat, muss er die Größe des Objekts
im Speicher wissen, sodass man Instanzen davon anlegen kann. Die
Funktions-Bodys können ja auf beliebige weitere Source-Dateien
(Translation Units) verteilt sein, und daher müsste der Compiler erst
alle Dateien durchgehen um die Größe eines Objekts zu bestimmen.
Torsten R. schrieb:> In dem Fall des OP handelt es sich um Funktions lokale, statische> Variablen und die gibt es auch schon in C. Hat also überhaupt nichts mit> Klassen oder Objekten zu tun.
Naja, es ist eben gerade keine "Funktions" lokale, statische Variable,
sondern eine "Methoden" lokale, statische Variable.
Dr. Sommer schrieb:> Das kann nicht so sein, denn: Nachdem der Compiler die "class"> Deklaration einer Klasse eingelesen hat, muss er die Größe des Objekts> im Speicher wissen, sodass man Instanzen davon anlegen kann. Die> Funktions-Bodys können ja auf beliebige weitere Source-Dateien> (Translation Units) verteilt sein, und daher müsste der Compiler erst> alle Dateien durchgehen um die Größe eines Objekts zu bestimmen.
Das stimmt natürlich, daran hatte ich gar nicht gedacht. Danke für den
Nachtrag.
Das ist dann auch ein weiterer Grund, warum Templates in der Deklaration
auch implementiert werden müssen.
1
#include<iostream>
2
#include<string>
3
4
template<inttemplateparameter>classKnopf
5
{
6
std::stringname;
7
public:
8
voiddrueck(){
9
staticsize_taufrufe=0;
10
++aufrufe;
11
std::cout<<"Knopf "<<name<<" wurde "<<aufrufe<<" mal gedrückt.\n";
12
}
13
Knopf(constchar*s):name(s){
14
}
15
};
16
17
intmain()
18
{
19
Knopf<1>ein("Ein");
20
ein.drueck();
21
ein.drueck();
22
// Jahre spaeter nachgeruestet, der Geraet hat einen Ausschalter bekommen:
nfet schrieb:> Torsten R. schrieb:>> In dem Fall des OP handelt es sich um Funktions lokale, statische>> Variablen und die gibt es auch schon in C. Hat also überhaupt nichts mit>> Klassen oder Objekten zu tun.>> Naja, es ist eben gerade keine "Funktions" lokale, statische Variable,> sondern eine "Methoden" lokale, statische Variable.
C++ kennt aber keine Methods, sondern nur functions (free functions and
member functions). Für das obige Beispiel gelten die Regeln für
functions.
> Das ist dann auch ein weiterer Grund, warum Templates in der Deklaration> auch implementiert werden müssen.
Müssen Sie doch überhaupt nicht. Es gibt auch eine Möglichkeit eine
member function eines class templates getrennt zu definieren:
Torsten R. schrieb:> Es gibt auch eine Möglichkeit eine member function eines class templates> getrennt zu definieren:
Ja, aber dennoch bevor sie aufgerufen wird...
Tom schrieb:> Für alle mitlesenden nur zur Sicherheit: Die static-Variable existiert> einmal für die ganze Klasse, nicht pro Objekt
Man kann (freien / Element-)Funktionen einen Zustand geben (wie hier),
man kann Objekten einen Zustand geben, man kann Klassen einen Zustand
geben, man kann Übersetzungseinheiten einen Zustand geben, man kann
einen programmglobalen Zustand haben.
Hier ist die Modellierung natürlich von Anfang an falsch, denn die
Anzahl der Tastendrücke sollte ein Objektzustand sein.
Dr. Sommer schrieb:> Torsten R. schrieb:>> Es gibt auch eine Möglichkeit eine member function eines class templates>> getrennt zu definieren:>> Ja, aber dennoch bevor sie aufgerufen wird...
Ich verstehe nicht ganz was Du meinst aber: man kann ein template auch
explizit instanziieren, dann muss die Definition der Funktion nicht in
der aktuellen Übersetzungseinheit bekannt sein.
Torsten R. schrieb:>> Ich verstehe nicht ganz was Du meinst aber: man kann ein template auch> explizit instanziieren, dann muss die Definition der Funktion nicht in> der aktuellen Übersetzungseinheit bekannt sein.
Du meinst wahrscheinlich eine Template-Spezialisierung; ja da geht das,
aber das ist auch kein "richtiges" template ;-) Und vorher deklarieren
muss man es auch.
Dr. Sommer schrieb:> Torsten R. schrieb:>>>> Ich verstehe nicht ganz was Du meinst aber: man kann ein template auch>> explizit instanziieren, dann muss die Definition der Funktion nicht in>> der aktuellen Übersetzungseinheit bekannt sein.> Du meinst wahrscheinlich eine Template-Spezialisierung;
Ob er das meint, weiß ich nicht. Gehen müßte es aber bei beiden.
Rolf M. schrieb:> Dr. Sommer schrieb:>> Torsten R. schrieb:>>>>>> Ich verstehe nicht ganz was Du meinst aber: man kann ein template auch>>> explizit instanziieren, dann muss die Definition der Funktion nicht in>>> der aktuellen Übersetzungseinheit bekannt sein.>> Du meinst wahrscheinlich eine Template-Spezialisierung;>> Ob er das meint, weiß ich nicht. Gehen müßte es aber bei beiden.
Funktionstemplates kann man explizit und implizit instanziieren.
Klassentemplates muss man explizit instanziieren, sofern man keine class
template deduction guides (C++17) verwendet.
Ist dem Compiler die Definition des templates bekannt, weil sie bspw. in
derselben(!) Übersetzungseinheit enthalten ist, so kann er das template
vollständig instanziieren. Ist ihm nur die Deklaration des templates
bekannt, so baut er ganz normal eine unaufgelöste Referenz ein und
verlässt sich darauf, dass die Definition an anderer Stelle ist und
durch den Linker aufgelöst werden kann. Wenn die Definition sich in
einer anderen Übersetzungseinheit befindet, so muss in dieser
Übersetzungseinheit die vollständige (explizite) Instanziierung
vorgenommen werden: sprich, der Programmierer muss wissen, welche
unterschiedlichen Instanziierungen im gesamten Programm vorkommen und
diese hier explizit machen. Das ganze ist also machbar, aber eigentlich
Quatsch (eine richtige Möglichkeit, templates extern zu definieren,
wurde nie in C++ aufgeommen).
Deswegen ist der "normale" Ansatz, die vollständige Definition des
Templates in einer(!) Header-Datei vorzunehmen. Ob man hier die
Deklaration einer Template-Elementfunktion von ihrer Definition trennt,
ist technisch unerheblich. Praktisch ist es bei umfangreicheren
Klassentemplates.
Templates führen grundsätzlich nicht zu ODR Problemen, da mehrfache
gleiche Instanziierungen in unterschiedlichen Übersetzungseinheiten
durch den Linker entfernt werden (ähnlich inline).
Dr. Sommer schrieb:> Du meinst wahrscheinlich eine Template-Spezialisierung; ja da geht das,> aber das ist auch kein "richtiges" template ;-) Und vorher deklarieren> muss man es auch.
Nein, ich meine "Explicit instantiation"
(http://en.cppreference.com/w/cpp/language/class_template). Meine
Antwort bezog sich ja auf:
>> Ja, aber dennoch bevor sie aufgerufen wird...
Welches ich als implizite Instanzierung interpretiert habe. Aber wie
gesagt: ich konnte den Kommentar eh nicht so richtig einordnen ;-)
Wilhelm M. schrieb:> Das ganze ist also machbar, aber eigentlich> Quatsch (eine richtige Möglichkeit, templates extern zu definieren,> wurde nie in C++ aufgeommen).
Doch, aufgenommen wurde so eine Möglichkeit in C++98. Sie wurde nur nie
signifikant von den Compilern unterstützt und ist deshalb inzwischen
wieder aus C++ entfernt worden.
Wilhelm M. schrieb:> sprich, der Programmierer muss wissen, welche> unterschiedlichen Instanziierungen im gesamten Programm vorkommen und> diese hier explizit machen. Das ganze ist also machbar, aber eigentlich> Quatsch ...
Für den (eher seltenen) Fall, dass die Menge der nötigen
Instanziierungen bekannt sind, würde ich explizite Instanziierung aber
immer bevorzugen, vor allem, wenn es komplexere Templates sind.
Torsten R. schrieb:> Wilhelm M. schrieb:>> sprich, der Programmierer muss wissen, welche>> unterschiedlichen Instanziierungen im gesamten Programm vorkommen und>> diese hier explizit machen. Das ganze ist also machbar, aber eigentlich>> Quatsch ...>> Für den (eher seltenen) Fall, dass die Menge der nötigen> Instanziierungen bekannt sind, würde ich explizite Instanziierung aber> immer bevorzugen, vor allem, wenn es komplexere Templates sind.
Und wozu?
Rolf M. schrieb:> Wilhelm M. schrieb:>> Das ganze ist also machbar, aber eigentlich>> Quatsch (eine richtige Möglichkeit, templates extern zu definieren,>> wurde nie in C++ aufgeommen).>> Doch, aufgenommen wurde so eine Möglichkeit in C++98. Sie wurde nur nie> signifikant von den Compilern unterstützt und ist deshalb inzwischen> wieder aus C++ entfernt worden.
Was ich damit ja sagen wollte.
export" ist seit C++11 wieder draussen.
Wilhelm M. schrieb:>> Für den (eher seltenen) Fall, dass die Menge der nötigen>> Instanziierungen bekannt sind, würde ich explizite Instanziierung aber>> immer bevorzugen, vor allem, wenn es komplexere Templates sind.>> Und wozu?
Kürzere Compile-Zeiten. Es geht ja nicht nur darum, dass der Compiler
ständig die gleichen Templates instanziieren muss. Wenn ich die
Implementierung in einer eigenen Übersetzungseinheit "verstecken" kann,
dann spare ich dem Compiler ja auch noch die ganzen header, die nur für
die Implementierung benötigt werden.
Der Fall kommt leider nicht sehr häufig vor, aber kommt vor. (z.B. in
der Nähe von Visitor-Patterns).
Torsten R. schrieb:> Wilhelm M. schrieb:>>> Für den (eher seltenen) Fall, dass die Menge der nötigen>>> Instanziierungen bekannt sind, würde ich explizite Instanziierung aber>>> immer bevorzugen, vor allem, wenn es komplexere Templates sind.>>>> Und wozu?>> Kürzere Compile-Zeiten.
Das sollten die Compiler eigentlich selbst können mit
precompiled-header.
> Es geht ja nicht nur darum, dass der Compiler> ständig die gleichen Templates instanziieren muss. Wenn ich die> Implementierung in einer eigenen Übersetzungseinheit "verstecken" kann,> dann spare ich dem Compiler ja auch noch die ganzen header, die nur für> die Implementierung benötigt werden.
Das für mich ausschlaggebende Argument wäre das "Verstecken" der
Implementierung, so dass ein Anwender nicht in die Versuchung gerät,
"gegen" die Implementierung eine Anwendung zu schreiben (das war die
Motivation von "export template").
Wilhelm M. schrieb:> Torsten R. schrieb:>> Wilhelm M. schrieb:>>>> Für den (eher seltenen) Fall, dass die Menge der nötigen>>>> Instanziierungen bekannt sind, würde ich explizite Instanziierung aber>>>> immer bevorzugen, vor allem, wenn es komplexere Templates sind.>>>>>> Und wozu?>>>> Kürzere Compile-Zeiten.>> Das sollten die Compiler eigentlich selbst können mit> precompiled-header.
Naja, wenn man sehr intensiv Templates nutzt, muss für jede
Übersetzungseinheit das halbe Programm nochmal gebaut werden, nur damit
das dann nachher vom Linker wieder alles verworfen wird. Mit precompiled
headers kostet das weniger Zeit, aber trotzdem kostet es noch Zeit.
Es hat ja durchaus seinen Grund, warum man ohne Templates auch nicht
einfach alles im Header implementiert.