Schalter mit Strukturen und Klassen
Transcription
Schalter mit Strukturen und Klassen
Stufenschalter mit Hilfe von Strukturen realisieren Hintergrundinformation: In modernen elektronischen Geräten werden heute nur noch selten mechanische Stufenschalter (Dreh- oder Schiebeschalter, siehe Abbildung links) verwendet. Mechanische Schalter sind groß, verschleißanfällig und teuer. Allerdings haben sie den Vorteil, dass sie auch direkt größere Lasten schalten können. Dies ist in elektronischen Geräten aber häufig gar nicht erforderlich. Input Channel 7 6 5 4 3 2 1 0 Stufenschalter werden heute in der Regel elektronisch, mittels Software oder einer Kombination aus beidem realisiert (siehe Abbildung links: „Kanalwahl“ eines Messgeräts). Soll ganz allgemein eine unter mehreren möglichen Betriebsarten eines Geräts gewählt werden, so bietet sich dabei folgende Lösung an: Die Auswahl erfolgt mit „Up“- und „Down“-Buttons, wobei die „Schalterstellung“ beispielsweise durch Leuchtdioden (oder numerisch mittels Display) angezeigt wird. Die oben genannten Nachteile mechanischer Schalter entfallen. Zusätzlich haben wir unter anderem folgende Vorteile: 1. Der Schalter kann leicht verriegelt werden, eine manuelle Änderung der aktuellen Schalterstellung ist dann nicht möglich. 2. Der Schalter kann (zum Beispiel beim Einschalten des Geräts) auf automatisch in eine Standardposition gebracht werden. 3. Der Schalter kann nicht nur vom Benutzer, sondern auch vom Gerät selbst betätigt werden. Aufgabe: a) Finden Sie eine Möglichkeit, Stufenschalter in C/C++ zu modellieren: Es soll möglich sein, beliebig viele Schalter zu „erzeugen“, für die unabhängig voneinander die minimale (minPos) und die maximale Schalterstellung (maxPos) eingestellt werden können. Die Schalterstellung soll direkt gewählt werden können (setPos), im Normalbetrieb werde allerdings schrittweise geschaltet (up, down). Der Schalter soll gesperrt (lock) und wieder freigegeben werden können (unlock). Die Schalterstellung stehe unter position verfügbar. setMinPos setMaxPos unlock lock setPos up down Schalter getPos z. B. Display b) Welche Fragen sind noch zu klären, um einen solchen Schalter mit eindeutig definierten Eigenschaften auszustatten. c) Welche Fehlersituationen können auftreten? Wie können Sie abgefangen werden? d) Welche weiteren sinnvollen(!) Features könnte man implementieren? Lösungsvorschlag: struct Switch { int pos; int minPos; int maxPos; bool locked; }; // // // // aktuelle schalterstellung minimale schalterstellung maximale schalterstellung status der verriegelung void defineSwitch(Switch *S, int min, int max, int pos) { S->minPos=min; S->maxPos=max; S->pos=pos; S->locked=false; } void up(Switch *S) { if(S->pos<S->maxPos && !S->locked) S->pos++; } void down(Switch *S) { if(S->pos>S->minPos && !S->locked) S->pos--; } void setPos(Switch *S, int pos) { S->pos=pos; } int getPos(Switch *S) { return S->pos; } void lock(Switch *S) { S->locked=true; } void unlock(Switch *S) { S->locked=false; } Definition und Initialisierung von Schaltern (Beispiel: Audio-Anwendung): // deklaration reserviert nur speicherplatz: Switch Volume, Bass, Mid, Treble; // initialisierung der strukturkomponenten mit defineSwitch() defineSwitch(&Volume, 0, 20, 0); defineSwitch(&Bass, -10, 10, 0); defineSwitch(&Mid, -10, 10, 0); defineSwitch(&Treble, -10, 10, 0); // // // // // // // // // // // // // // // // // //// // // // // // // optional: initialisierung bei globaler deklaration (bei lokaler deklaration compilerabhaengig) Switch Volume { 0, // 0, // 20, // false // }; = aktuelle schalterstellung minimale schalterstellung maximale schalterstellung status der verriegelung ebenso moeglich: Switch Volume = {0, 0, 20, false}; ebenso fuer die uebrigen schalter weitere option: Volume.pos=0; Volume.minPos=0; Volume.maxPos=20; Volume.locked=false; // // // // aktuelle schalterstellung minimale schalterstellung maximale schalterstellung status der verriegelung ebenso fuer die uebrigen schalter Nachteile des Strukturkonzepts ??? 1. Unkontrollierter Zugriff auf Strukturkomponenten möglich! 2. Strukturkomponenten können versehentlich mit unzulässigen Werten belegt werden 3. Beliebige Funktionen können hinzugefügt werden und das Konzept „verwässern“! NEU: Lösungsvorschlag mit Klassen: class Switch // neues { private: // neues int pos; // int minPos; // int maxPos; // bool locked; // schluesselwort class schluesselwort private aktuelle schalterstellung minimale schalterstellung maximale schalterstellung status der verriegelung public: // neues schluesselwort public // hier: nur deklaration(!) von elementfunktionen void defineSwitch(int inpMin, int inpMax, int inpPos); void up(void); void down(void); void setPos(int inpPos); int getPos(void); void lock(void); void unlock(void); }; // definition der elementfunktionen, neuer operator :: void Switch::defineSwitch(int inpMin, int inpMax, int intPos) { minPos=inpMin; maxPos=inpMax; pos=inpPos; locked=false; } void Switch::up(void) { if(pos<maxPos && !locked) pos++; } void Switch::down(void) { if(pos>minPos && !locked) pos--; } void Switch::setPos(int inpPos) { pos=inpPos; } int Switch::getPos(void) { return pos; } void Switch::lock(void) { locked=true; } void Switch::unlock(void) { locked=false; } // definition von Switch-objekten: Switch Volume, Bass, Mid, Treble; // aufruf: Volume.defineSwitch(0, 20, 0); // etc. NEU: Klassen mit Konstruktor: Werden beispielsweise mit class Volume, Bass, Mid, Treble; Objekte der Klasse Switch erzeugt , so müssen die Attribute anschließend noch durch Aufrufe der Elementfunktion defineSwitch(...) initialisiert werden. Geschieht dies nicht, so kann es zu schweren Fehlern kommen. Ein Konstruktor ist eine spezielle Elementfunktion, die automatisch bei der Erzeugung eines Objekts aufgerufen wird. Sie unterscheidet sich von den anderen Elementfunktionen einer Klasse durch zwei Besonderheiten: 1. Der Name eines Konstruktors ist identisch mit dem Klassennamen. 2. Ein Konstruktor besitzt keinen Ergebnistyp, auch nicht void! Die Deklaration erfolgt gewöhnlich im public-Bereich. Konstruktoren können wie andere Funktionen überladen werden. Die Konstruktoren einer Klasse müssen sich durch ihre Signatur (d. h. Anzahl, Reihenfolge und Typ der Parameter) unterscheiden. Damit können Objekte auf verschiedene Arten initialisiert werden Ein Konstruktor, für den keine Parameter deklariert werden, heißt Default-Konstruktor. Er wird aufgerufen, wenn bei der Definition eines Objekts keine Initialisierungswerte angegeben werden. Der Default-Konstruktor wird gewöhnlich so geschrieben, dass er alle Datenelemente auf Default-Werte setzt. Wenn für eine Klasse kein Konstruktor definiert ist, erzeugt der Compiler eine MinimalVersion des Default-Konstruktors als public-Element, der allerdinsg keine Anfangswerte setzt. Ist aber wenigstens ein Konstruktor definiert, so muss auch der Default-Konstruktor explizit definiert werden, wenn er zur Verfügung stehen soll. Wird wie unten für die Klasse Switch kein Default-Konstruktor definiert, so muss(!) bei der Definition eines Objekts eine Initialisierungsliste angegeben werden. class Switch // nun { private: int pos; int minPos; int maxPos; bool locked; mit konstruktor // // // // aktuelle schalterstellung minimale schalterstellung maximale schalterstellung status der verriegelung public: Switch(int inpMin, int inpMax, int inpPos); void up(void); void down(void); void setPos(int inpPos); int getPos(void); void lock(void); void unlock(void); }; //definition des konstruktors Switch::Switch(int inpMin, int inpMax, int intPos) { minPos=inpMin; maxPos=inpMax; pos=inpPos; locked=false; } // definition von Switch-objekten: Switch Switch Switch Switch Volume(0, 20, 0); Bass(-10, 10, 0); Mid(-10, 10, 0); Treble(-10, 10, 0);