Benutzer-Werkzeuge

Webseiten-Werkzeuge


skript:klassen-arduino

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen gezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
skript:klassen-arduino [2018/10/07 21:05]
d.golovko
skript:klassen-arduino [2021/06/11 11:28] (aktuell)
d.golovko
Zeile 14: Zeile 14:
 class Song { class Song {
   public:   public:
-    Song(); // Konstruktor, muss noch mit Parametern versehen werden+    Song(int noteList[], int arraySize, int pinNumber); // Konstruktor
     void play(); // Methode zum Abspielen der Melodie     void play(); // Methode zum Abspielen der Melodie
   private:   private:
-    int notes[]; // Noten-Frequenzen in Hz+    int *notes; // Noten-Frequenzen in Hz; ist ein Zeiger, da Array-Laenge noch nicht bekannt 
 +    int arraySize; // Laenge des Noten-Arrays
     int pin; // Piezo-Pin     int pin; // Piezo-Pin
 }; };
Zeile 24: Zeile 25:
 <file cpp Song.cpp>​ <file cpp Song.cpp>​
 #include "​Song.h"​ #include "​Song.h"​
-Song::​Song() {+Song::Song(int noteList[], int arraySize, int pinNumber) {
   // TODO: Implementierung des Konstruktors fehlt   // TODO: Implementierung des Konstruktors fehlt
 } }
Zeile 36: Zeile 37:
  
 Ihr sieht oben, dass die Header-Datei zwei Blöcke hat, die mit ''​public''​ und ''​private''​ anfangen. Diese Schlüsselworte weisen auf die [[https://​de.wikipedia.org/​wiki/​Sichtbarkeit_(Programmierung)|Sichtbarkeit]] der Variablen und Funktionen hin. ''​Public''​ bedeutet, dass die Variable/​Funktion nur innerhalb der Klasse sichtbar ist, ''​protected''​ innerhalb der Klasse und ihrer Unterklassen,​ ''​public''​ überall. (Ähnliche Ebenen gibt es übrigens auch in Java.) Der Faustregel ist, die Sichtbarkeit möglichst begrenzt zu wählen. Die Begründung ist die gleiche wie bei globalen und lokalen Variablen: Je mehr Stellen haben Zugriff auf die Variable, desto mehr fehleranfällig ist der Code. Wir machen den Konstruktor und die Funktion ''​play()''​ ''​public'',​ damit wir darauf von unserem Haupt-Tab zugreifen können. Die beiden Variablen sind ''​private'',​ sie sollen nur innerhalb der Klasse verfügbar sein. Es ist üblich, in der Header-Datei den ''​public''​-Block ganz oben zu platzieren. ​ Ihr sieht oben, dass die Header-Datei zwei Blöcke hat, die mit ''​public''​ und ''​private''​ anfangen. Diese Schlüsselworte weisen auf die [[https://​de.wikipedia.org/​wiki/​Sichtbarkeit_(Programmierung)|Sichtbarkeit]] der Variablen und Funktionen hin. ''​Public''​ bedeutet, dass die Variable/​Funktion nur innerhalb der Klasse sichtbar ist, ''​protected''​ innerhalb der Klasse und ihrer Unterklassen,​ ''​public''​ überall. (Ähnliche Ebenen gibt es übrigens auch in Java.) Der Faustregel ist, die Sichtbarkeit möglichst begrenzt zu wählen. Die Begründung ist die gleiche wie bei globalen und lokalen Variablen: Je mehr Stellen haben Zugriff auf die Variable, desto mehr fehleranfällig ist der Code. Wir machen den Konstruktor und die Funktion ''​play()''​ ''​public'',​ damit wir darauf von unserem Haupt-Tab zugreifen können. Die beiden Variablen sind ''​private'',​ sie sollen nur innerhalb der Klasse verfügbar sein. Es ist üblich, in der Header-Datei den ''​public''​-Block ganz oben zu platzieren. ​
 +
 +Wenn in C++ Arrays als Parameter an eine Funktion übergeben werden, wird nur der Zeiger auf das erste Array-Element übergeben. D.h. die Funktion bekommt keine Kopie des ganzen Arrays, sondern nur die Adresse des ersten Elementes. Eine Lösung ist, die Array-Länge als einen weiteren Parameter (''​arraySize''​ in unserem Beispiel) zu übergeben. In der Liste der Membervariablen wird ebenso nur ein Zeiger ''​int *notes''​ verwendet, da die Deklaration eines Arrays ohne seiner Länge nicht möglich ist. 
  
 ====2. Implementierung==== ====2. Implementierung====
Zeile 46: Zeile 49:
 // Konstruktor;​ noteList: Notenabfolge,​ arraySize: Laenge des Noten-Arrays,​ pin: Piezo-Pin // Konstruktor;​ noteList: Notenabfolge,​ arraySize: Laenge des Noten-Arrays,​ pin: Piezo-Pin
 Song::​Song(int noteList[], int arraySize, int pinNumber) { Song::​Song(int noteList[], int arraySize, int pinNumber) {
-  ​for (int i 0; i < arraySize; ​i++) { +  ​this->​arraySize ​= arraySize;​ 
-    this->​notes[i] = noteList[i]; +  this->​notes = noteList;
-  }+
   this->​pin = pinNumber;   this->​pin = pinNumber;
 } }
 +
 //[...] //[...]
 </​file>​ </​file>​
- 
-Dementsprechend müssen wir die Konstruktordeklaration auch in Zeile 3 der Header-Datei ändern: \\ 
-''​Song(int noteList[], int arraySize, int pinNumber);''​ 
  
 Genauso wie in Java wird ''​this''​ benutzt, um auf die Instanz der Klasse (d.h. auf die gerade erzeugte Objekt vom Typ ''​Song''​) zu zeigen. Achtung: in C++ wird statt Punkt ein Pfeil verwendet.  ​ Genauso wie in Java wird ''​this''​ benutzt, um auf die Instanz der Klasse (d.h. auf die gerade erzeugte Objekt vom Typ ''​Song''​) zu zeigen. Achtung: in C++ wird statt Punkt ein Pfeil verwendet.  ​
Zeile 63: Zeile 63:
 #include "​Arduino.h"​ #include "​Arduino.h"​
 //[...] //[...]
 +// Spielt die Melodie ab
 void Song::​play() { void Song::​play() {
-  for (int i = 0; i < sizeof(notes) / sizeof(notes[0]); i++) { +  for (int i = 0; i < arraySize ​; i++) { 
-    tone(pin, notes[i]500); // jede Note spielt 0,5 Sekunde+    tone(pin, notes[i]); 
 +    delay(500);
   }   }
 +  noTone(pin);​
 } }
 //[...] //[...]
Zeile 78: Zeile 81:
 // Konstruktor;​ noteList: Notenabfolge,​ arraySize: Laenge des Noten-Arrays,​ pin: Piezo-Pin // Konstruktor;​ noteList: Notenabfolge,​ arraySize: Laenge des Noten-Arrays,​ pin: Piezo-Pin
 Song::​Song(int noteList[], int arraySize, int pinNumber) { Song::​Song(int noteList[], int arraySize, int pinNumber) {
-  ​for (int i 0; i < arraySize; ​i++) { +  ​this->​arraySize ​= arraySize;​ 
-    this->​notes[i] = noteList[i]; +  this->​notes = noteList;
-  }+
   this->​pin = pinNumber;   this->​pin = pinNumber;
 } }
Zeile 86: Zeile 88:
 // Spielt die Melodie ab // Spielt die Melodie ab
 void Song::​play() { void Song::​play() {
-  for (int i = 0; i < sizeof(notes) / sizeof(notes[0]); i++) { +  for (int i = 0; i < arraySize ​; i++) { 
-    tone(pin, notes[i]500); // jede Note spielt 0,5 Sekunde+    tone(pin, notes[i]); 
 +    delay(500);
   }   }
 +  noTone(pin);​
 } }
 </​file>​ </​file>​
Zeile 96: Zeile 100:
 ====3. Aufruf==== ====3. Aufruf====
  
-Nun werden wir einen Song im Haupt-Tab erstellen und aufrufen, z.B. so:+Nun werden wir einen Song im Haupt-Tab erstellen und aufrufen, z.B. so (Piezo auf Pin 2):
  
-<file cpp haupttab>+<file cpp variante1>
 #include "​Song.h"​ #include "​Song.h"​
  
 void setup() { void setup() {
-  int notes[] = {300400500600700}; // Notenfrequen+  int notes[] = {262294330349392}; // Notenfrequenzen
   Song mySong (notes, sizeof(notes) / sizeof(notes[0]),​ 2); // oder: Song mySong(notes,​ 5, 2);   Song mySong (notes, sizeof(notes) / sizeof(notes[0]),​ 2); // oder: Song mySong(notes,​ 5, 2);
   mySong.play();​   mySong.play();​
 +}
 +
 +void loop() { 
 +}
 +</​file>​
 +
 +Achtung: wir müssen dem Compiler mit ''#​include Song.h''​ Bescheid sagen, dass eine Klasse aus einer anderen Datei verwendet wird. Falls wir auf die Variable ''​mySong''​ sowohl von ''​setup()''​ als auch von ''​loop()''​ zugreifen wollen, können wir diese inkl. Konstruktoraufruf auch global haben:
 +<file cpp variante2>​
 +#include "​Song.h"​
 +int notes[] = {262, 294, 330, 349, 392};
 +Song mySong (notes, sizeof(notes) / sizeof(notes[0]),​ 2); // oder: Song mySong(notes,​ 5, 2);
 +
 +void setup() {
 +  mySong.play(); ​
 } }
  
 void loop() { void loop() {
 +  mySong.play();​
 } }
 </​file>​ </​file>​
 +
 +Der Konstruktor hier wird aufgerufen, bevor ''​setup()''​ anfängt. Das kann ungünstig sein im Fall, wenn der Konstruktor Parameter hat, deren Werte am Anfang des Programms noch nicht bekannt sind (z.B. wenn sie von Sensormessungen abhängen, die in ''​setup()''​ gemacht werden). Anders als in Java, können wir nicht einfach ''​Song mySong;''​ in Zeile 3 schreiben. Das liegt daran, dass ''​mySong''​ eine Referenz ist, und Referenzen dürfen nicht gleich NULL sein. 
 +
 +<​note>​
 +Die Unterscheidung zwischen Referenzen und Zeigern (Englisch: pointers) in C++ ist ein separates großes Thema -- bei Interesse lesst [[https://​www.geeksforgeeks.org/​pointers-vs-references-cpp/​|diesen Link]] oder sucht Informationen dazu online. Wir werden dieses Thema hier nicht angehen. ​
 +</​note>​
 +
 +Wir werden stattdessen einen //Zeiger// nutzen -- man kann das so verstehen, dass, statt des Objektes an sich, haben wir nur einen Verweis darauf, wo dieses Objekt sich befindet. Ein Zeiger wird mit einem ''​*''​ markiert, und der Zugriff auf Funktionen und Variablen geht über ''​->''​.
 +
 +<file cpp variante3>​
 +#include "​Song.h"​
 +Song* mySong; // Zeiger
 +
 +void setup() {
 +  int notes[] = {262, 294, 330, 349, 392};
 +  mySong = new Song(notes, sizeof(notes) / sizeof(notes[0]),​ 2); // oder: Song mySong(notes,​ 5, 2);
 +  mySong->​play(); ​
 +}
 +
 +void loop() {
 +  mySong->​play();​
 +}
 +</​file>​
 +
skript/klassen-arduino.1538939141.txt.gz · Zuletzt geändert: 2018/10/07 21:05 von d.golovko