Inhalt:
Hinweis auf Attribute im allgemeinen Sprachgebrauch: ``hat'', z.B. ein Fahrzeug hat eine Farbe.
Durch die Kapselung ist es möglich, Funktionale Einheiten in C++ abzubilden, die durch ihre Eigenschaften definiert und so jederzeit wiederverwendet werden können.
Eine Klasse bestimmt den Datentyp allgemein, aehlich einer "struct" in C. C++ Schlüsselwort: "class"
Die bei Variablen und Structs kann Speicher via malloc/free und -- bei Objekten bevorzugt -- mittels new/delete belegt und freigegeben werden.
Hinweis auf Vererbung im allgemeinen Sprachgebrauch: ``ist ein'', z.B. ein Auto ist ein Fahrzeug.
class Fahrzeug {
private:
// Attribute:
char *farbe;
public:
// Methoden:
void setFarbe(char *f);
void print(void);
};
void Fahrzeug::setFarbe(char *f)
{
farbe = f;
}
void Fahrzeug::print(void)
{
cout << "Fahrzeug: " << farbe << endl;
}
int main(void)
{
Fahrzeug f, f2;
f.setFarbe("gruen"); // setzt f.farbe
f.print();
f2.setFarbe("blau"); // setzt f2.farbe
f2.print();
return 0;
}
Fahrzeug *p;
p = (Fahrzeug *)malloc(sizeof(Fahrzeug));
p->setFarbe("rot");
p->print();
free(p);
p = new Fahrzeug;
p->setFarbe("weiss");
p->print();
delete p;
class Fahrzeug {
private:
// Attribute:
char *farbe;
public:
// Methoden:
void setFarbe(char *f){
farbe = f;
}
void print(void){
cout << "Fahrzeug: " << farbe << endl;
}
};
Dies eignet sich jedoch nur für kurze Funktionen.
Fahrzeug crash;
crash.print();
class Fahrzeug {
private:
// Attribute:
char *farbe;
public:
// Methoden:
Fahrzeug(void);
Fahrzeug(char *f);
void setFarbe(char *f);
void print(void);
};
Fahrzeug::Fahrzeug(void)
{
farbe = NULL;
}
Fahrzeug::Fahrzeug(char *f)
{
farbe = f;
}
void Fahrzeug::print(void)
{
cout << "Fahrzeug: " << (farbe==NULL?"unlackiert":farbe) << endl;
}
...
int main(void)
{
Fahrzeug unlackiert1; // Fahrzeug(void)
unlackiert1.print();
Fahrzeug *unlackiert2 = new Fahrzeug; // Fahrzeug(void)
unlackiert2->print();
Fahrzeug ferrari("rot"); // Fahrzeug(char *)
ferrari.print();
Fahrzeug *porsche = new Fahrzeug("schwarz"); // Fahrzeug(char *)
porsche->print();
return 0;
}
Ausgabe:
% ./fahrzeug2 Fahrzeug: unlackiert Fahrzeug: unlackiert Fahrzeug: rot Fahrzeug: schwarz(Quellcode)
class Fahrzeug {
public:
// Attribute:
char *farbe;
// Methoden:
Fahrzeug(void);
Fahrzeug(char *f);
~Fahrzeug(void);
void setFarbe(char *f);
void print(void);
};
Fahrzeug::~Fahrzeug(void)
{
cout << "Verschrotte Fahrzeug in "
<< (farbe?farbe:"unlackiertes") << endl;
}
...
///////////////////////////////////////////////////////////////////////////
// Eine globale, statisch alloziierte Variable
Fahrzeug himmelswagen("gelb");
int main(void)
{
Fahrzeug f1("rot");
Fahrzeug *kitt = new Fahrzeug("schwarz");
{
Fahrzeug titanic("schwarz");
himmelswagen.print();
f1.print();
titanic.print();
kitt->print();
// Ende Gueltigkeit titanic
}
delete kitt; // Ende Gueltigkeit kitt
return 0;
// Ende Gueltigkeit f1;
}
// Ende Gueltigkeit himmelswagen
Ausgabe:
% ./fahrzeug3 Fahrzeug: gelb Fahrzeug: rot Fahrzeug: schwarz Fahrzeug: schwarz Verschrotte Fahrzeug in schwarz Verschrotte Fahrzeug in schwarz Verschrotte Fahrzeug in rot Verschrotte Fahrzeug in gelb(Quellcode)
#include <stdio.h>
class Huhn
{
public:
Huhn(void) { printf("Gack!\n"); }
~Huhn(void) { printf("Gick!\n"); }
} huhn;
int main(void)
{
printf("Ei ei ei!\n");
return 0;
}
Ausgabe:
% ./huhn-und-ei Gack! Ei ei ei! Gick!(Quellcode)
void Fahrzeug::setFarbe(char *farbe)
{
this->farbe = farbe;
}
class Fahrzeug {
static int globalcnt;
int nummer;
public:
// Attribute:
char *farbe;
// Methoden:
Fahrzeug(char *f = NULL);
void print(void);
};
int Fahrzeug::globalcnt = 0;
Fahrzeug::Fahrzeug(char *f)
{
farbe = f;
globalcnt++;
nummer = globalcnt;
}
void Fahrzeug::print(void)
{
cout << "Fahrzeug " << nummer << "/" << globalcnt << ": "
<< (farbe?farbe:"unlackiert") << endl;
}
int main(void)
{
Fahrzeug unlackiert1;
Fahrzeug *unlackiert2 = new Fahrzeug;
Fahrzeug ferrari("rot");
Fahrzeug *porsche = new Fahrzeug("schwarz");
unlackiert1.print();
unlackiert2->print();
ferrari.print();
porsche->print();
return 0;
}
Ausgabe:
% ./fahrzeug4 Fahrzeug 1/4: unlackiert Fahrzeug 2/4: unlackiert Fahrzeug 3/4: rot Fahrzeug 4/4: schwarz(Quellcode)
Weiteres zur UML: UML, Klassendiagramm, Objektdiagramm.
class Kraftfahrzeug : public Fahrzeug {
public:
// Zusätzliche Attribute:
int leistung;
// Methoden:
Kraftfahrzeug(char *f = NULL, int l=0)
: Fahrzeug(f) // Erst Konstruktor der Basisklasse
{ leistung = l; };
void setLeistung(int l){ leistung = l; }
void print(void){
cout << "Kraftfahrzeug: " << leistung << "PS, ";
Fahrzeug::print();
}
};
Kraftfahrzeug *kfz = new Kraftfahrzeug("rot", 120);
kfz->print();
Ausgabe:
Kraftfahrzeug: 120PS, Fahrzeug: rot
Fahrzeug *f1 = kfz; // OK!
f1->print();
Ausgabe:
Fahrzeug: rot
Was ist hier passiert? Der Compiler hat Fahrzeug::print()
aufgerufen, da er nicht wusste dass f1 in wirklichkeit auf ein
Kraftfahrzeug zeigt. Dies kann ihm mittels virtueller Funktionen
mitgeteilt werden, siehe unten!
(Quellcode)
kfz = f1; // Fehler!
class Fahrzeug {
public:
...
~Fahrzeug(void) {
cout << "Verschrotte Fahrzeug in "
<< (farbe?farbe:"unlackiert") << endl;
}
...
};
class Kraftfahrzeug : public Fahrzeug {
public:
...
~Kraftfahrzeug(void) {
cout << "Verschrotte Kraftfahrzeugs-Motor mit " << leistung
<< "PS" << endl;
}
...
};
int main(void)
{
Kraftfahrzeug *kfz = new Kraftfahrzeug("rot", 120);
kfz->print();
delete kfz;
return 0;
}
Ausgabe:
Kraftfahrzeug: 120PS, Fahrzeug: rot
Verschrotte Kraftfahrzeugs-Motor mit 120PS
Verschrotte Fahrzeug in rot
(Quellcode)
class Amphibienfahrzeug : public Landfahrzeug, public Wasserfahrzeug {
public:
// Zusätzliche Attribute:
// keine
// Methoden:
Amphibienfahrzeug(char *f = NULL, int r=4, double t=0.0)
: Landfahrzeug(f, r), Wasserfahrzeug(f, t) { }
~Amphibienfahrzeug(void) {
cout << "Hole Amphibienfahrzeug ans Ufer" << endl; }
void print(void){
cout << "Amphibienfahrzeug:" << endl ;
cout << "\t"; Wasserfahrzeug::print();
cout << "\t"; Landfahrzeug::print();
}
};
int main(void)
{
Fahrzeug f("rot"); // KF
Landfahrzeug lf("blau", 4); // KL
Wasserfahrzeug wf("gruen", 1.0); // KW
Amphibienfahrzeug af("lila", 6, 0.5); // KA
f.print(); // PF
lf.print(); // PL
wf.print(); // PW
af.print(); // PA
cout << endl;
return 0;
// Destruktoren DA DW DL DF
}
Ausgabe:
PF: Fahrzeug: rot PL: Landfahrzeug: 4 Raeder, Fahrzeug: blau PW: Wasserfahrzeug: 1m Tiefgang, Fahrzeug: gruen PA: Amphibienfahrzeug: PA: Wasserfahrzeug: 0.5m Tiefgang, Fahrzeug: lila PA: Landfahrzeug: 6 Raeder, Fahrzeug: lila -- DA: Hole Amphibienfahrzeug ans Ufer DA/DW: Hebe Wasserfahrzeug 0.5m DA/DW/DF: Verschrotte Fahrzeug in Farbe lila -- DA/DL: Verschrotte 6 Landfahrzeug-Raeder DA/DL/DF: Verschrotte Fahrzeug in Farbe lila -- DW: Hebe Wasserfahrzeug 1m DW/DF: Verschrotte Fahrzeug in Farbe gruen -- DL: Verschrotte 4 Landfahrzeug-Raeder DL/DF: Verschrotte Fahrzeug in Farbe blau -- DF: Verschrotte Fahrzeug in Farbe rot(Quellcode)
Kraftfahrzeug *kfz = new Kraftfahrzeug("rot", 120);
Fahrzeug *f = kfz;
f->print();
Obwohl "f" eigentlich auf ein Kraftfahrzeug zeigt war die Ausgabe:
Fahrzeug: rot
Was gefragt waere ist eine Möglichkeit, in der Objekt-Instalz (== im Speicher) selbst zu hinterlegen, von welcher Klasse sie ist, und wie die zugehörige print()-Routine ist, ohne sich auf die statische Analyse des Compilers zu verlassen.
class Fahrzeug {
public:
...
virtual void print(void){
cout << "Fahrzeug: " << (farbe?farbe:"unlackiert") << endl;
}
};
class Kraftfahrzeug : public Fahrzeug {
// Bleibt unverändert
};
int main(void)
{
Fahrzeug *f1;
Kraftfahrzeug *kfz = new Kraftfahrzeug("rot", 120);
f1 = kfz;
f1->print(); // Ruft Kraftfahrzeug::print() auf
Ausgabe:
Kraftfahrzeug: 120PS, Fahrzeug: rot
Kraftfahrzeug *kfz = new Kraftfahrzeug("rot", 120);
Fahrzeug *f = kfz;
delete f;
Ausgabe:
Verschrotte Fahrzeug in rot
class Fahrzeug {
public:
...
virtual ~Fahrzeug(void){
cout << "Verschrotte Fahrzeug in "
<< (farbe?farbe:"unlackiert") << endl;
}
Mit dieser Änderung ergibt der obrige delete-Aufruf:
Verschrotte Kraftfahrzeugs-Motor mit 120PS
Verschrotte Fahrzeug in rot
(Quellcode)
Objekte solcher rein virtuellen Klassen können nicht instantiiert werden!
class Fahrzeug {
public:
...
virtual void start(void) = 0;
...
};
class Flugzeug {
public:
...
void start(void){ cout << "Ready for take-off!" << endl; }
...
};
int main(void)
{
Flugzeug flieger; // OK
Fahrzeug auto; // Fehler zur Compile-Zeit
Speichern Sie die Klasse in in der Datei "liste.h". Benutzen Sie die Klasse mit dem Programm liste-test.cc, das eine Listen anlegt, und anschliessend bis zum erreichen des Dateiendes Zahlen von der Tastatur einliesst, und diese in die Liste einhaengt. Anschliessend soll die Liste ausgegeben werden:
% make liste-test ... % ./liste-test Zahl eingeben: 17 Zahl eingeben: 42 Zahl eingeben: 1 Zahl eingeben: 2 Zahl eingeben: 3 Zahl eingeben: -2 Zahl eingeben: -1 Zahl eingeben: 0 Zahl eingeben: 55 Zahl eingeben: ^D Zahlen: 55 0 -1 -2 3 2 1 42 17 %(Lösungsvorschlag: liste.h)
Benutzen Sie das Testprogramm stack-test.cc, das einen Stack anlegt. Das Programm liest Zahlen von Tastatur ein, und legt sie auf den Stack. Bei erreichen des Dateiendes werden alle Zahlen auf dem Stack ausgegeben:
% make stack-test ... % ./stack-test 17 42 33 ^D Stack: 33 42 17 | > 33 > 42 > 17 %(Lösungsvorschlag: stack.h)