Enum

Enum ist ein Gebilde der prozeduralen Programmierung, mit dem eine Kette von verbalen Symbolen
einer Kette von Integer - Zahlen zugeordnet wird. Der Name enum ist eine Abkürzung für
"enumeration", und das ist = = Aufzählung.

Wenn ich z.B. sage

enum {Languste, Shrimp, Hummer, Taschenkrebs, Krabbe};

ohne Eigennamen nach dem "enum", dann wird nur die Aufzählung angelegt.
Wenn ich aber einen Eigennamen benutze, z.B.:

enum Tascheninhalt{taschenmesser, tote_maus, taschentuch, einmachgummi};

erschaffe ich zugleich den neuen Datentyp "Tascheninhalt".

Es folgt ein funktionierendes Beispiel für Enum:


#include<iostream.h>

void main()
{
enum Ampel{rot, gelb, gruen};
int a;

do{
cout<<"geben Sie eine Zahl zwischen 0 und 2 ein:> ";
cin>>a;
switch(a)
       {
           case rot: cout<<"rot"<<endl; break;
           case gelb: cout<<"gelb"<<endl; break;
           case gruen: cout<<"gruen"<<endl; break;
        }
}

while(1);

}

Man kann in diesem Beispiel das enum tatsächlich nicht weglassen, weil man sonst nicht mehr
case rot:
case gelb:
case gruen:
sagen darf! Probiert doch mal aus, die Zeile
enum Ampel{rot, gelb, gruen};
auszukommentieren.
Man kriegt dann insgesamt 6 Compiler-Fehler und eine Warnung:
Bezeichner "rot" nichtdeklarierter Bezeichner!usw.
Man braucht also enum, um erst den int a als Selector im switch - Konstrukt zu haben und dann
case rot:
case gelb:
case gruen: schreiben zu können!
---->In diesem Beispiel wird die interne Arbeitsweise von enum tatsächlich deutlich. q.e.d.

Namespaces

Man kann fertige Namensbereiche benutzen, Bsp.:

using namespace std;

oder eigene Namensbereiche anlegen

namespace MeinName
{
    Funktionen,
    Variable,
    Alles!!!
}

Aufruf:

using namespace MeinName;

Das Erschaffen eigener Namespaces kann so oft geschehen, wie man will und
wir dürfen die namespaces auch ineinander verschachteln!

Wenn man #include<iostream> schreibt, also das ". h" weglässt,
und nun trotzdem nicht using namespace std; angibt, kann man die Variablen
des Namespace std trotzdem noch mit Hilfe des Scope resolution operators : : ansprechen, etwa wie folgt

std: :eineVariable;



Objekte

Elementfunktion - Memberfunktion - Methode
Eine Methode ist in der Objektorientierung das, was im prozeduralen C eine Funktion war.

Eigenschaften - Datenelemente
Das sind unsere Variablen, bzw. Objektvariablen.

Klasse - Objekttyp
Ein Objekttyp, Oberbegriff, abstrakt

Instanz einer Klasse - Objekt einer Klasse
Eine Instanz ist ein Objekt vom Datentyp einer Klasse.
Damit ist eine Instanz ein konkreter Begriff.

Eigenschaften, die mit den Fähigkeiten nicht in irgend einem Zusammenhang stehen,
braucht ein Computerobjekt nicht.
Jedes Datenelement muss man daher einer kritischen Prüfung unterziehen, ob es von
einer Elementfunktion genutzt wird.
Auch die Rückgabewerte von Funktionen sollte man nur benutzen, wenn man sie wirklich braucht.
Sonst immer void!!
Solange es noch keine Instanz gibt, ist eine Klasse nur eine Idee, ein Objekttyp.
Der Konstruktor belegt alle Datenelemente des Objekts mit sinnvollen Anfangswerten.
Man braucht Konstruktor und Destruktor, wenn man am Anfang und Ende eines Objektes
mehr als nichts tun will.
Die Datensicherheit wird erhöht(-->kontrollierter Datenzugriff). Der Destruktor kann auch
fragen: "Wollen Sie das Programm wirklich schließen?"
Konstruktor und Destruktor sind Funktionen, für uns also Elementfunktionen bzw. Methoden.
Wir brauchen daher, so wie bei jeder anderen Funktion Deklaration, Implementierung und Aufruf.
Eine Klasse ist eine Datenstruktur, die Datenelemente und Funktionen enthält.
Ihre Vorgänger waren struct und typedef.

kontrollierter Datenzugriff
1) Datenelemente initialisieren
2) Datenelementen Werte zuweisen
3) Datenelement - Werte lesen
4) Datenelemente aufräumen

Ein kleines objektorientiertes Programm

#include<iostream>

using namespace std;

class Robbi /*Deklaration der Klasse Robbi*/
{
int amount_of_courtesy; //Variablen sind defaultmäßig private
public: /*Funktionen sollen public sein, damit ich sie auch in der main verwenden kann!*/
void Welcome();
Robbi(int init_amount_of_courtesy);//Deklaration des Konstruktors
void Aendern(int init_amount_of_courtesy);
};

Robbi::Robbi(int init_amount_of_courtesy)/*Konstruktor, man beachte, dass der Konstruktor den gleichen Namen wie die Klasse hat!*/
{
     amount_of_courtesy=init_amount_of_courtesy;/*Der Konstruktor weist dem Datenelement einen init - Wert zu.*/
}

void Robbi::Aendern(int init_amount_of_courtesy)//Die Memberfunktion Aendern
{
     amount_of_courtesy=init_amount_of_courtesy;
}

void Robbi::Welcome()//Die Memberfunktion Welcome
{

     switch(amount_of_courtesy){/*Das private Datenelement amount_of_courtesy ist in der Memberfunktion Welcome bekannt und kann daher der Selektor meiner Switch Anweisung werden.*/
     case 0: exit(-1);
     case 1: cout<<"Sackgesicht!\n\n"; break;
     case 2: cout<<"Arsch mit Ohren!\n\n"; break;
     case 3: cout<<"Stinker!\n\n"; break;
     case 4: cout<<"kleiner Frechdachs!\n\n"; break;
     case 5: cout<<"ein ganz Netter!\n\n"; break;
     case 6: cout<<"Hallo, wie gehts?\n\n"; break;
     case 7: cout<<"hi!\n\n"; break;
     case 8: cout<<"Du bist ein Objekt der Klasse Mensch!\n\n"; break;
     case 9: cout<<"Schaetzchen!\n\n"; break;
     case 10: cout<<"Liebling!\n\n"; break;
     default: cout<<"Klugscheisser, 99 Punkte\n\n\n ";
     }
}

void main()
{
     int grad, i; /*Die Variable i ist der Zähler für die for - Schleife, grad wird benötigt, weil amount_of_courtesy als private Variable in der main Fkt nicht bekannt ist.*/
     Robbi Golem1(1);//Erschaffung Golem1 mit dem Initialisierungswert 1(Sackgesicht)
     Robbi Golem2(3);//Erschaffung Golem2 mit dem Initialisierungswert 3(Stinker)

     Golem1.Welcome();
     for (i=0;i<100;i++)//Die for - Schleife bewirkt, dass man sich bis zu 100 mal von dem Golem beschimpfen lassen kann
     {
     cout<<"Bitte waehlen Sie einen Grad der Hoeflichkeit,"<<endl<<"mit dem Sie adressiert werden moechten! 1 - 10"
            <<endl<<"0 fuer beenden"<<endl;
     cin>>grad;

    // Robbi Golem2(grad);
    // Golem2.Welcome();//Zwei auskommentierte Experimente
     Golem2.Aendern(grad);
     Golem2.Welcome();
     }
}


Der Einsatz eines Destruktors schien für ein Programm dieser Größe noch nicht sinnvoll.


Nun zum Kreuz der Kreuze(crux crucorum, oder so), nämlich der Syntax :

Wann nehme ich den Scope resolution operator, wann den Punkt, wann nur ein Leerzeichen?
Fragen über Fragen, denen wir nun aber zu Leibe rücken wollen.
Wenn ich eine Variable vom Typ einer Klasse erschaffen will(d.i. eine Objektvariable, meist Objekt
oder Instanz genannt), schreibe ich den Namen der Klasse und dann den Namen der Variablen,
wie gehabt, mit Leerzeichen dazwischen, z.B.:

Robbi Golem2; FEHLER!!!

Die Klammer darf nicht fehlen, weil der Konstruktor sonst keinen
Initialisierungswert erhält! Richtig muss hier also stehen:
Robbi Golem2( ); außer wenn ich keinen expliziten Konstruktor schreibe
und damit den impliziten default - Konstruktor verwende, dann wäre Robbi Golem2; richtig.
Weil der Konstruktor von Golem2 einen Parameter vom Typ int entgegennehmen kann, den er dann in seine
private Variable amount_of_courtesy einliest, schreibe ich z.B.:

Robbi Golem2(grad); Die Variable grad ist auch vom Typ int. Sie initialisiert das Objekt
mit einem "sinnvollen" Wert.
Den Scope resolution operator brauche ich, um meine Methoden bei der Implementierung an die Klasse zu ketten, also z.B.:

void Robbi : : Welcome( )

Den Punktoperator brauche ich nun beim Aufruf einer Methode bzw. Elementfunktion, was nur auf einer Instanz geht, also z.B.:

Golem1.Welcome( );


Fazit:

Leerzeichen - beim Bilden von Instanzen einer Klasse

Punktoperator - beim Aufrufen einer Methode auf einer Instanz

Scope Resolution Operator - beim Implementieren von Methoden einer Klasse


Nachschlag : Wo stehen eigentlich die Semikola???

Ein Semikolon steht nach der Deklaration einer Klasse hinter den geschweiften Klammern.
Kein Semikolon steht nach der Implementierung der Methode einer Klasse, ebenfalls
hinter den geschweiften Klammern.

In einer Klasse sind die Elemente Eigenschaften(d.h. Variablen) oder Fähigkeiten(d.h. Funktionen).
private deklarierte Eigenschaften oder Fähigkeiten sind nur innerhalb der Klasse und allen ihren
Instanzen, bzw. Objekten, Objektvariablen bekannt.
public deklarierte Fähigkeiten sind die Schnittstellen einer Klasse und deren Objekte nach außen, also z.B. für die main( ) Funktion.
C++ stellt auch eine Vielzahl fertiger Objekte zur Verfügung, um uns auch die objektbasierte
Programmierung zu ermöglichen.
Die Standardfunktionen cout und cin aus iostream sind solche Objekte.


Der Unterschied zwischen einer Objektvariablen und einer Elementvariablen ist der :

- Eine Objektvariable ist eine Variable vom Typ einer Klasse.
- Eine Elementvariable ist eine Variable, die wenn sie private ist, nur
innerhalb dieser Klasse gilt und existiert und wenn sie -aus welchem Grund auch immer-
public ist, auch noch woanders.
Mit anderen Worten, die Elementvariable ist eine "kleine" Variable in einer Klasse
mit all ihren Objektvariablen. Eine Objektvariable ist nämlich eine Instanz einer Klasse,
die ich auch von der Klasse instanziere, natürlich ohne dass dies eine Rangordnung impliziert.
Diese Variable ist dann ein "dicker Hoschi", der alles enthält, was die Klasse auch hat.
Die Elementvariable ist nur der kleine normalerweise private Knirps in der Klasse.


Auf die Verwendung von public - Variablen soll man in der OOP(Objektorientierte Programmierung)
möglichst nur dann zurückgreifen, wenn es wirklich triftige Gründe gibt, um nämlich
die Prinzipien der Kapselung und data hiding nicht zu unterlaufen.
Die Kapselung ermöglicht die einfache Handhabung von Objekten, da es sich um echte "all-in-one"
Komponenten handelt. Ich muss mir keine Gedanken mehr über irgendwelche internen Variablen machen,
ich kriege eine oder mehrere Schnittstellen in Form von Methoden - und schoooon fertig!
Daher ist die Verwendung globaler Variablen in der OOP in philosophischer Hinsicht ein
"Rückfall in die prozedurale Denke".

Ein interessanter Aspekt von OOP ist auch, dass man beim OOD(Object oriented Design) manchmal
glaubt, sich zwischen zwei Möglichkeiten entscheiden zu müssen, während die Lösung wirklich ist,
beides zu nehmen.
Wenn ich z.B. eine Klasse Adressverwaltung habe, die alles Nötige für mein Adressverwaltungsprogramm
können soll und mich frage "Brauche ich nun eine Klasse Adressverwaltung oder nenne ich die Klasse lieber
Adressen?", kann ich auch gut damit fahren, einfach Beides anzulegen.

Beispiel für eine Klasse mit 3 public Elementen:

class Itchy{
   public:
   char pfeil;
   string bombe;
   void Scratchy();
};

Der komfortable Umgang mit String - Objekten

Wie wir seit dem IT - Kindergarten wissen, wird ein String eigentlich intern als
array of Characters realisiert.
Da C als echter "do-it-yourself" Baukasten den Datentyp string nicht enthält, musste
man etwa so vorgehen:

char die_allein_selig_machende_programmiersprache[50];

Dabei war zu beachten, dass ein Zeichen für '\0' reserviert bleiben musste. Einen Überlauf
des Arrays mit zu vielen Elementen musste man auch vermeiden!

C++ ermöglicht uns, einfach zu schreiben:

#include <string>
und dann eine ganze Reihe komfortabler Möglichkeiten mit Strings zu nutzen. Man darf natürlich
using namespace std; nicht vergessen!
Die header Datei string.h ohne namespace ist in ihren Möglichkeiten begrenzt und interessiert
daher jetzt nicht.
Der entscheidende Punkt ist, dass ich nun plötzlich einen Datentyp string habe.
Ich darf also wie folgt deklarieren:

string ursel, babsi, monika, tutti_frutti; oder so, ohne Klammer, ohne Elemente zählen!
chön!

Außerdem kann ich nun Methoden der String - Klasse verwenden. Wenn ich z.B. ursel.length( ) tippe,
sagt mir ursel, wie lang sie ist.
Wenn ich getline(cin, tutti_frutti); schreibe, kriege ich eine ganze Textzeile in die Variable
tutti_frutti eingelesen.

Möchte ich toupper(Großbuchstb) oder tolower(Kleinbuchstb) verwenden, so wird die entsprechende
Header - Datei nun als #include <cctype> eingebunden(mit using namespace std;).

Vgl. hier! So haben wir das früher gemacht.

Bei der Arbeit mit Objekten erübrigt es sich tatsächlich weitgehend
1. enum zu benutzen
2. eigene namespaces zu definieren und
3. zur Unterbringung unterschiedlicher Variablentypen in einem zusammenhängenden
  Gebilde Strukturen zu verwenden.

Deeper into C++