Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
skript:klassen-arduino [2018/10/26 17:51] d.golovko [1. Deklaration] |
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 arraySize; // Laenge des Noten-Arrays | ||
int pin; // Piezo-Pin | int pin; // Piezo-Pin | ||
Zeile 25: | 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 37: | 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 47: | 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 64: | 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 79: | 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 87: | 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 127: | Zeile 130: | ||
</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 daran, dass ''mySong'' eine Referenz ist, und Referenzen dürfen nicht gleich NULL sein. | + | 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> | <note> |