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);