Hallo,
ich habe mal eine Frage zu einem konkreten Beispiel und möchte wissen,
wie ihr das handhaben würdet. Ich habe eine Klasse und programmiere sehr
Hardware nah. Zur Zeit läuft die Software auf einem Raspberry PI, jedoch
würde ich den Code gerne auf einem kleinen ARM Cortex benutzen. Hier
geht es nun konkret über Klassenvariablen. Wie verwendet man diese
korrekt und übersichtlich.
Angenommen var1,var2,var3,var4 sind Arrays die jeweils 40 Byte Speicher
im RAM benötigen (20 x uint16_t). Die Funktion "eineFunktion" wird in
der Sekunde zwischen 100-500x aufgerufen. Welches Beispiel ist dann
besser?
Was gehört als Klassenvariable deklariert und was als Funktionsvariable?
Klassenvariable: Das was auch andere Funktionen benötigen könnten?
Funktionsvariable: Belanglose cnt Variablen, nur Variablen die die
Funktion benötigt, die außerhalb nicht benötigt werden?
EDIT:
Wann macht IHR eine Variable public und wann private?
BEISPIEL A:
Man sollte variabel möglichst nur dort anlegen wo sie gebraucht werden.
Wenn sie nur in einer Methode verwendet werden dann dort, wenn sie in
der Klasse gebraucht werden dann dort oder im schlimmsten fall sogar
global.
Bei Klassen sollte alle Variablen private oder Protected sein. Diese
werden nur über Methoden geändert. sonst hat man ja nicht die Kapselung
die man will.
int var1=0;
int var2=1;
int var3=2;
int var4=3;
ich habe vor langer Zeit mal gelernt, dass man sowas auch nicht
unbedingt machen sollte.
Initialisierung und Zuweisung passieren an zwei völlig unterschiedlichen
Laufstellen, besser wäre folgendes
int var1;
int var2;
int var3;
int var4;
var1=0;
var2=1;
var3=2;
var4=3;
Waldemar schrieb:> int var1=0;> int var2=1;> int var3=2;> int var4=3;>> ich habe vor langer Zeit mal gelernt, dass man sowas auch nicht> unbedingt machen sollte.
ich glaube sogar das geht in C++ gar nicht, bei C# ist das zulässig.
So macht man es sinnvollerweise:
Danke für die Antworten, aber um die Zuweisung geht es mir hier nicht.
Das war nur ein Beispiel. Klar kann man das über den Konstruktor machen.
Ich wollte nur mal wissen, wie ihr das mit den Variablen Handhabt.
Jetzt wurde angesprochen:
Peter II schrieb:> Bei Klassen sollte alle Variablen private oder Protected sein. Diese> werden nur über Methoden geändert. sonst hat man ja nicht die Kapselung> die man will.
Diesen Vorgang verstehe ich auch nicht so ganz. Welchen Vorteil bringt
meine eine Memberfunktion, die mir den Inhalt einer Variable ausgibt?
Die brauche ich ja zwingend um den Inhalt einer Variable, die Private
ist aus der Klasse zu bekommen.
ich könnte machen:
int EineKlasse::getValue()
{
return var1;
}
a=getValue.EineKlasse();
ODER
a=var1.EineKlasse; <- Das ist doch viel effizienter?
Thomas P. schrieb:> Diesen Vorgang verstehe ich auch nicht so ganz. Welchen Vorteil bringt> meine eine Memberfunktion,
dann man die Variabel in der Klasse einfach ändern kann und das
Interface nach außen gleich bleibt.
Wenn du public ist, kann sie auch von außen geändert werden, das will
man aber nicht.
> a=var1.EineKlasse; <- Das ist doch viel effizienter?
mit der richtige Optimierung vom Compiler kommt bei beiden das gleiche
Raus.
In deinen beiden Klassen werden komplett unterschiedliche Dinge getan,
das hat nichts mit public/private zu tun.
Beispiel A:
Hier hast du nur eine public Funktion und eine private Variable welche
nie verwendet wurde. Die Variabeln var1 bis var4 sind nur innerhalb der
Funktion gültig und sichtbar, was nichts mit public/private zu tun hat.
Da diese Variabeln auch nur in der Funktion gültig sind, können sie
nicht von aussen verändert werden, die Funktion gibt immer den gleichen
Wert zurück.
Beispiel B:
4 public Variablen, eine public Funktion und wieder eine private
Variable, welche nie benutzt wird.
Durch eine Instanz der Klasse kannst du auf die Variabeln var1 bis var4
zugreifen:
1
EineKlasse KonkreteKlasse;
2
KonkreteKlasse.var2 = 12;
3
cout << KonkreteKlasse.eineFunktion() << endl;
Peter II schrieb:> ich glaube sogar das geht in C++ gar nicht, bei C# ist das zulässig.>> So macht man es sinnvollerweise:
+1 für initializer list
Thomas P. schrieb:> Angenommen var1,var2,var3,var4 sind Arrays die jeweils 40 Byte Speicher> im RAM benötigen (20 x uint16_t). Die Funktion "eineFunktion" wird in> der Sekunde zwischen 100-500x aufgerufen. Welches Beispiel ist dann> besser?
Deine return var1; geht sowieso nicht mit der Funktionsvariablen, wenn
das ein Array von 20 Werten ist, denn das Array ist nicht mehr im
Speicher nachdem die Funktion verlassen wurde. Als einfacher int geht
das Beispisl schon. Daher ist dein Beispiel völliger Blödsinn zur
Darstellung der Problematik und du kommst auf völlig falsche Lösungen.
Die Frage ist, wie lange deine Variablen var1, var2, var3 und var4
gültig sein sollen, und wie viele es davon gibt.
Eine Klassenvariable wie du sie nennst ist so lange gültig wie es die
Instanz der Klasse gibt (also länger als die Funktion) und es gibt so
viel davon wie es Instanzen der Klasse gibt. Dafür kostet so eine
Variable Speicher im RAM von heap.
Eine Funktionsvariable ist nur bis zum Ende der Funktion vorhanden, bei
Rückkehr aus der Funktion wird sie vergessen, dafür kostet sie RAM im
stack.
Meine Theorie: Dein Programm hat sowieso nur 1 Instanz der Klasse und
diese Instanz ist vorhanden vom Programmanfang bis zum Programmende.
Dann stellt sich die Frage, warum es nicht globale Variablen sein
sollten.
Gibt es IRGEDEINEN realen Grund, warum nicht ?
wenn du mehrere Instanzen der KLasse haben könntet und dann diese
Variablen 2 3 oder 4 mal da sein müssen -> müssen es Klassenvariablen
sein.
Wenn die Funktion eineFunktion sich entweder selbst rekursiv aufruft
oder eventuell simultan in mehreren treads bzw. prozessen parallel
läuft, muss es eine Funktionsvariable sein.
Wenn es eine Phase im Programm gibt, in der diese Variablen benötigt
werden (z.B. Datenerfassungsphase), dann aber eine andere Phase im
Programm gibt, in denen man die Variablen nicht mehr braucht (z.B.
Datenvisualisierungsphase) aber den Speicherplatz unbedingt für andere
Variablen benötigt, kann man nicht beide gleichezitig als globale
Varablen mit einer Gültigkeit von vor dem Programmstart bis nach dem
Programmende halten.
Wenn weder - noch als Grund dasteht, kann es eine gloabel Variable sein.
Die liegen zwar auch im RAM, müssen aber nicht bei jedem Funktionsaufruf
alloziert und ggf. initialisiert werden, sie sind gültig und bleiben
erhalten wenn die Funktion verlassen wird, und da der Speicher schon
beim Kompilieren reserviert ist kann es damit nicht passieren, daß
inmiten des Programmlaufs diese Variablen nicht mehr allozierbar sind.
Kein heap wird benutzt, der stack nicht übermässig aufgebläht und die
Zugriffe erfolgen an absolute statt relative Adreessen und sind damit
schneller. Globale Variablen haben also viele vor allem
Effizienzvorteile, die man nicht ohne Grund herschenken soltle.
Achsoja, dein Prozessor hat eh die 10000-fache Rechenleistung von der
die nötig ist, also muss amn die auch verblasen....
- Auf var1-4 von außen zugreifen? -> Public. Zugriff über
Eineklasse::var
- var1-4 nur innerhalb der Klasse benötigt? -> Private
- var1-4 nur innerhalb der Klasse benötigt? -> Private
- var1-4 nur innerhalb von eineFunktion benötigt? -> dort deklarieren
- var1-4 nur innerhalb von eineFunktion benötigt, Werte sollen zwischen
den Aufrufen erhalten bleiben? -> dort static deklarieren
Hoffe ich erinnere mich richtig. Ist ein bisschen her.
Es gibt noch weitere Möglichkeiten, aber ich glaube das führt zu weit.
Operator S. schrieb:> Hier hast du nur eine public Funktion und eine private Variable welche> nie verwendet wurde.
Ja, habe nie was anderes behauptet
Operator S. schrieb:> Die Variabeln var1 bis var4 sind nur innerhalb der> Funktion gültig und sichtbar
Ja, habe nie was anderes behauptet
Operator S. schrieb:> was nichts mit public/private zu tun hat.
Habe das auch damit nie in den Zusammenhang gebracht
Operator S. schrieb:> Da diese Variabeln auch nur in der Funktion gültig sind, können sie> nicht von aussen verändert werden, die Funktion gibt immer den gleichen> Wert zurück.
Ja auch das hast du vollkommen richtig analysiert gut!
Operator S. schrieb:> Beispiel B:> 4 public Variablen, eine public Funktion und wieder eine private> Variable, welche nie benutzt wird.
Richtig um die private Variable geht auch hier auch nicht.
Ich wollte nur wissen was effizienter ist Beispiel A oder B. Mit der
Annahme das var1-var4 z.B. große Arrays sind.
MaWin schrieb:> Deine return var1; geht sowieso nicht mit der Funktionsvariablen, wenn> das ein Array von 20 Werten ist, denn das Array ist nicht mehr im> Speicher nachdem die Funktion verlassen wurde. Als einfacher int geht> das Beispisl schon. Daher ist dein Beispiel völliger Blödsinn zur> Darstellung der Problematik und du kommst auf völlig falsche Lösungen.
Ich gehe in meinem REALEN Beispiel in eine Funktion und schreibe 20
Werte in ein Array danach berechne ich den Mittelwert und gebe diesen
als INT zurück. Das var1-var4 in meinem Beispiel kein Array ist, ist
völlig klar. Ich hatte jetzt keine lust den 200 Zeilen langen Code zu
posten, deswegen diese "Vereinfachung"
Meine Frage war nur: Was ist effizienter: Ständig 500 mal in der Sekunde
neuen Speicher zu reservieren und wieder frei zu geben (Variable in
Funktion) oder dies einmalig (global)
Thomas P. schrieb:> Meine Frage war nur: Was ist effizienter: Ständig 500 mal in der Sekunde> neuen Speicher zu reservieren und wieder frei zu geben (Variable in> Funktion) oder dies einmalig (global)
das kann niemand pauschal sagen, auf dem Stack kostet das anlegen von
variabel keine Zeit.
JJ schrieb:> - Auf var1-4 von außen zugreifen? -> Public. Zugriff über> Eineklasse::var> - var1-4 nur innerhalb der Klasse benötigt? -> Private> - var1-4 nur innerhalb der Klasse benötigt? -> Private> - var1-4 nur innerhalb von eineFunktion benötigt? -> dort deklarieren> - var1-4 nur innerhalb von eineFunktion benötigt, Werte sollen zwischen> den Aufrufen erhalten bleiben? -> dort static deklarieren>> Hoffe ich erinnere mich richtig. Ist ein bisschen her.> Es gibt noch weitere Möglichkeiten, aber ich glaube das führt zu weit.
und
Peter II schrieb:> Thomas P. schrieb:>> Meine Frage war nur: Was ist effizienter: Ständig 500 mal in der Sekunde>> neuen Speicher zu reservieren und wieder frei zu geben (Variable in>> Funktion) oder dies einmalig (global)>> das kann niemand pauschal sagen, auf dem Stack kostet das anlegen von> variabel keine Zeit.
Bis jetzt die besten Antworten. Danke! Ich denke das kann man
zusammenfassend so stehen lassen :-)
Thomas P. schrieb:> Ich gehe in meinem REALEN Beispiel in eine Funktion und schreibe 20> Werte in ein Array danach berechne ich den Mittelwert und gebe diesen> als INT zurück.> Meine Frage war nur: Was ist effizienter: Ständig 500 mal in der Sekunde> neuen Speicher zu reservieren und wieder frei zu geben (Variable in> Funktion) oder dies einmalig (global)
Dann mache das Array doch statisch in der Funktion.
Thomas P. schrieb:> Im Prinzip müsste nur intValue nach außen bekannt sein. Also mache ich> die Public in meiner Klasse
wozu? was gefällt dir an dem return nicht?
Und wie willst du überhaupt ein Methodenmember public machen?
Thomas P. schrieb:> Ich gehe in meinem REALEN Beispiel in eine Funktion und schreibe 20> Werte in ein Array danach berechne ich den Mittelwert und gebe diesen> als INT zurück. Das var1-var4 in meinem Beispiel kein Array ist, ist> völlig klar. Ich hatte jetzt keine lust den 200 Zeilen langen Code zu> posten, deswegen diese "Vereinfachung"
Deine "Vereinfachung" war aber eine "Verunklarung". Offensichtlich
brauchst du also die 80 Werte mit Beendigung der Funktion nicht mehr.
Also können es Funktionsvariablen sein. Es gibt keinen sachlichen
Grund,. warum es Klassenvariablen sein sollten, denn andere
Klassenfunktionen brauchen die Werte offenbar nicht.
> Meine Frage war nur: Was ist effizienter: Ständig 500 mal in der Sekunde> neuen Speicher zu reservieren und wieder frei zu geben (Variable in> Funktion) oder dies einmalig (global)
Wenn die Funktion sowieso lokale Variablen hat, ist es egal, ob da noch
80 Byte mehr allokiert werden, ob da also ADD StackPointer,5 oder ADD
StackPointer,80 steht und die ggf. eingeschaltete Stacküberlaufprüfung
folgt.
Wenn sie keine lokalen Variablen hat (oder so wenige, daß der Compiler
sie alle in Register wegoptimiert, was er bei den 80 ints im Array nicht
kann), kostet es mehr Zeit.
Der ZUGRIFF auf die Werte ist bei globalen Variablen an absoluten
Adressen aber bei quasi allen Prozessoren schneller, manchmal deutlich
schneller.
Eine Klassenvariable ist aber noch keine globale Variable.
Peter II schrieb:> wozu? was gefällt dir an dem return nicht?>> Und wie willst du überhaupt ein Methodenmember public machen?
Ähhh moment! Ja... kleine Änderung
Diese Funktion soll dann 100-500x in der Sekunde aufgerufen werden.
die Variable "current" ist nun eine public Varible der Klasse Channel
Thomas P. schrieb:> Channel::readActualCurrent()
Ich stolpere immer schwer über solche tolle "Germanismen"
Gemeint ist wohl "akteller Strom", der Englisch sprechende
versteht aber unter "actual" "tatsächlich", wogegen "aktuell"
im Englischen "current" heisst ....
Bitwurschdler schrieb:> Gemeint ist wohl "akteller Strom", der Englisch sprechende> versteht aber unter "actual" "tatsächlich", wogegen "aktuell"> im Englischen "current" heisst ....
Jaaaa natürlich hast du recht :-), ich werde mir da was passenderes
überlegen.
MaWin schrieb:> Deine "Vereinfachung" war aber eine "Verunklarung". Offensichtlich> brauchst du also die 80 Werte mit Beendigung der Funktion nicht mehr.> Also können es Funktionsvariablen sein. Es gibt keinen sachlichen> Grund,. warum es Klassenvariablen sein sollten, denn andere> Klassenfunktionen brauchen die Werte offenbar nicht.
Ok. Ich dachte dadurch etwas einsparen zu können.
Bitwurschdler schrieb:> Gemeint ist wohl "akteller Strom", der Englisch sprechende> versteht aber unter "actual" "tatsächlich", wogegen "aktuell"> im Englischen "current" heisst ..
Also readCurrentCurrent() ;-)
Gustavo F. schrieb:> Bitwurschdler schrieb:>> Gemeint ist wohl "akteller Strom", der Englisch sprechende>> versteht aber unter "actual" "tatsächlich", wogegen "aktuell">> im Englischen "current" heisst ..>> Also readCurrentCurrent() ;-)
Das dachte ich mir auch :D... Wollte es mir nur verkneifen.
Habe es jetzt readCurrent genannt ;-)
Bitwurschdler schrieb:> Ich stolpere immer schwer über solche tolle "Germanismen">> Gemeint ist wohl "akteller Strom", der Englisch sprechende> versteht aber unter "actual" "tatsächlich", wogegen "aktuell"> im Englischen "current" heisst ....
Ich bin mir gar nicht so sicher, ob 'actual' hier tatsächlich so falsch
ist. 'actual' bedeutet nämlich 'tatsächlich', 'derzeitig',
'gegenwärtig', 'effektiv'... Habe auch schon öfter gesehen, dass
Variablen das im Namen haben.
Gustavo F. schrieb:> Habe auch schon öfter gesehen, dass Variablen das im Namen haben.
Klar, weil nämlich die Mehrheit der deutschen Programmierer so
stümperhaft Englisch beherrschen.
Ein Amerikaner oder Brite würde das nie so ausdrücken.
Es sei denn es gilt einen "unwirklichen" und einen "tatsächlichen"
Wert zu unterscheiden.
Frickelfritze schrieb:> Ein Amerikaner oder Brite würde das nie so ausdrücken.
Und wenn man "aktuell", "gegenwärtig", "laufend" meint stimmt
"actual" überhaupt nicht.
Thomas P. schrieb:> Diese Funktion soll dann 100-500x in der Sekunde aufgerufen werden.>> die Variable "current" ist nun eine public Varible der Klasse Channel
Warum? Handelt es sich dabei um einen Zustand, der sich gemerkt werden
muss? Soweit ich die Funktion überblicke, sehe ich nichts, was
erfordert, dass sie ein Klassen-Member ist.
Thomas P. schrieb:> void Channel::readActualCurrent()> {> uint16_t offset=25;> uint32_t average=0;> uint16_t var[20]={0};
....
> var[cnt]=readBuffer[0] << 8 | readBuffer[1];> average=average+var[cnt];> }> average=average/20;> current=(((average*0.0001875*1000)-2500)*2)-offset;> //6.144/(2^15-1)=0.0001875> }> }
wozu überhaupt die var variabel? Wenn du wirklich etwas sparen willst,
dann lass die Variabel doch ganz weg.
tictactoe schrieb:> Warum? Handelt es sich dabei um einen Zustand, der sich gemerkt werden> muss? Soweit ich die Funktion überblicke, sehe ich nichts, was> erfordert, dass sie ein Klassen-Member ist.
Ist auch recht schwer ohne eine Glaskugel, wenn du nicht weißt was der
Rest von meinem Programm macht.
Ja. Sie speichert den aktuellen Strom. Andere Programmteile können dann
auf diese Variable zugreifen. Bevor jetzt tausend Leute wieder
hinterfragen ob das nötig oder unnötig ist, möchte ich alle Zweifel
beseitigen. Die hälfte des Threads beschäftigt sich sowieso nur mit der
Sinnigkeit gewisser Formulierungen oder diverser anderer Dinge, die mir
nicht helfen die Programmierung optimaler zu machen.
Ich habe einige Sensorknoten die ich regelmäßig auslese. Da sich diese
Sensorknoten nur in der Hardwareadresse unterscheiden, macht es wenig
Sinn dies ohne Klassen zu lösen. So übergebe ich der Klasse mit dem
Konstruktor die Hardwareadresse und fertig ist mein Sensor. So kann ich
dann schnell auf alle Ströme/Spannungen/Temperaturen whatever zugreifen.
1
a=sensor1.current;
2
a=sensor2.current;
3
a=sensor3.current;
4
a=sensor4.current;
5
a=sensor5.current;
6
7
oderalsGet-Funktion
8
9
a=sensor1.getCurrent();
10
...
11
a=sensor5.getCurrent();
Ja? Wäre ohne Klassen bisschen mehr Aufwand...
Da ich mir die Messwerte mit ncurses in der Shell anzeigen lasse,
rattert im Hintergrund eine Schleife, die mir ständig die Messwerte
aktualisiert. Darf ich das jetzt weiterhin mit einer Klasse machen?
Danke :-)
Peter II schrieb:>> void Channel::readActualCurrent()>> {>> uint16_t offset=25;>> uint32_t average=0;>> uint16_t var[20]={0};> ....>> var[cnt]=readBuffer[0] << 8 | readBuffer[1];>> average=average+var[cnt];>> }>> average=average/20;>> current=(((average*0.0001875*1000)-2500)*2)-offset;>> //6.144/(2^15-1)=0.0001875>> }>> }
Du hast recht! Ich kann ja auch das machen, was den selben Effekt hat:
Ich muss nur aufpassen das die Laufvariable der for-Schleife den selben
wert die der Teiler durch [average] ist. das werde ich dann noch über
eine Hilfsvariable lösen.
Danke Peter!!!
Thomas P. schrieb:> Da sich diese> Sensorknoten nur in der Hardwareadresse unterscheiden, macht es wenig> Sinn dies ohne Klassen zu lösen. So übergebe ich der Klasse mit dem> Konstruktor die Hardwareadresse und fertig ist mein Sensor. So kann ich> dann schnell auf alle Ströme/Spannungen/Temperaturen whatever> zugreifen.
Abenteuerliche Begründung.
Wer nur Nägel kennt, haut auch Schrauben mit dem Hammer rein.
> Ich muss nur aufpassen das die Laufvariable der for-Schleife den selben> wert die der Teiler durch [average] ist. das werde ich dann noch über> eine Hilfsvariable lösen.
Ich würde erst mal aufpassen, daß
> current=(((average*0.0001875*1000)-2500)*2)-offset;
^
da kein float steht, auch nicht 0.1875.
Sondern eher (average*1875)/10000 oder gleich
current=(((average*1875)/5000)-5000-offset;
Mit unint32_t sollte average ja gross genug sein.
Da kann man, wenn man geschickter formuliert, sicher auch uint16_t draus
machen.
Es ist ja noch nicht mal klar, ob dein "kleiner Cortex" überhaupt eine
FPU hätte.
Der Weg, vom ehemals durch Windows, Java, C++ und WebSkripten
verseuchten Gehirn hin zu effizientem coden ist noch weit...
Thomas P. schrieb:> Ich muss nur aufpassen das die Laufvariable der for-Schleife den selben> wert die der Teiler durch [average] ist. das werde ich dann noch über> eine Hilfsvariable lösen.
das kann man über ein Define oder eine const int variabel lösen. Wenn du
die Wahl hast kannst du über 16 werte mitteln, dann kann die Devision
durch shift (vom Compiler) ersetzt werden.
Michael B. schrieb:> Ich würde erst mal aufpassen, daß>>> current=(((average*0.0001875*1000)-2500)*2)-offset;> ^> da kein float steht, auch nicht 0.1875.>> Sondern eher (average*1875)/10000 oder gleich>> current=(((average*1875)/5000)-5000-offset;>> Mit unint32_t sollte average ja gross genug sein.> Da kann man, wenn man geschickter formuliert, sicher auch uint16_t draus> machen.
Danke!
> Es ist ja noch nicht mal klar, ob dein "kleiner Cortex" überhaupt eine> FPU hätte.
Ich möchte auf dem Raspberry so programmieren, dass es mir später leicht
fallen soll, den Code auf einen µC zu portieren. Zumindest die
Sensorverwaltung
> Der Weg, vom ehemals durch Windows, Java, C++ und WebSkripten> verseuchten Gehirn hin zu effizientem coden ist noch weit...
Ja
Peter II schrieb:> das kann man über ein Define oder eine const int variabel lösen. Wenn du> die Wahl hast kannst du über 16 werte mitteln, dann kann die Devision> durch shift (vom Compiler) ersetzt werden.
Vielen Dank!