DAP2-ProgrammierpraktikumEinführung in C++ (Teil 2)
Carsten Gutwenger18. April 2008
Lehrstuhl 11 – Algorithm EngineeringFakultät für Informatik, TU Dortmund
Carsten Gutwenger: Einführung in C++ (Teil 2) 2
Überblick
• Dynamischer Speicher
• Klassen und Strukturen
– Const-Korrektheit
– Virtuelle Funktionen
• Überladen von Operatoren
• Generische Programmierung
Carsten Gutwenger: Einführung in C++ (Teil 2) 3
Dynamischer Speicher
Zwei Arten von Speicher
• Stack
– lokale Variablen, Funktionsparameter
– Lebensdauer: bis Funktionsende
• Heap– dynamisch angefordert mit new
– Lebensdauer: bis Aufruf von delete
Carsten Gutwenger: Einführung in C++ (Teil 2) 4
Einfaches Beispiel
struct Pair { int x, y };
Pair *createPair() {
Pair *p = new Pair;
p->x = 17; p->y = 4;
return p;
}
void usePair() {
Pair *q = createPair();
cout << "pair is " << q->x << ", " << q->y << endl;
delete q;
}
Speicher für Pair auf Heap anlegen
Speicher für Pair auf Heap wieder freigeben
Carsten Gutwenger: Einführung in C++ (Teil 2) 5
Beispiel mit Feldern
int *createArray(int n) {
int *a = new int[n];
for(int i = 0; i < n; ++i)
a[i] = i;
return a;
}
void useArray() {
int *a = createArray(100);
for(int i = 0; i < 100; ++i)
cout << "a[" << i << "] = " << a[i] << endl;
delete [] a;
}
new-Operator für Felder
delete-Operator für Felder
Carsten Gutwenger: Einführung in C++ (Teil 2) 6
Zusammenfassung
• Dynamischer Speicher wird mit new angelegt und mit deletewieder freigegeben.
• Beim Anlegen eines Objektes können auch Parameter an den Konstruktor übergeben werden:– Pair *p = new Pair(x,y);
• Dynamische Felder– Werden mit der Array-Form des new-Operators angelegt.
– Müssen mit der Array-Form des delete-Operators freigegeben werden.
– Für Elemente wird immer der Default-Konstruktor aufgerufen.
Carsten Gutwenger: Einführung in C++ (Teil 2) 7
Klassen und Strukturen
struct A {
int x; // public
private:
int y;
};
class A {
int x; // private
public:
int y;
};
Default-Sichtbarkeit:
• struct: public
• class: private
Carsten Gutwenger: Einführung in C++ (Teil 2) 8
Wichtige Member-Funktionen
• Konstruktoren:– Default-Konstruktor: Pair();
– Copy-Konstruktor: Pair(const Pair &x);
– weitere: Pair(int x, int y);
• Destruktor:– Wird vor dem Freigeben des Speichers für das Objekt aufgerufen
– ~Pair();
• Operatoren (z.B. Zuweisungsoperator)– später!
• Achtung: Werden teilweise automatisch erstellt!– Default- und Copy-Konstruktor, Zuweisungsoperator
Carsten Gutwenger: Einführung in C++ (Teil 2) 9
Erzeugen von Objekten einer Klasse
• Merke: Klassen sind vollwertige Datentypen und können wie primitive Datentypen verwendet werden!– Keine Notwendigkeit für new, wenn nur eine lokale Variable benötigt
wird; dann braucht man sich auch nicht um delete zu kümmern.
class Pair { ... };
Pair a; // Default-Konstruktor
Pair b(22,33); // selbst-definierter Konstruktor
Pair c(a); // Copy-Konstruktor
Pair *d = new Pair(b); // Copy-Konstruktor
...
delete d; // nicht vergessen!
Carsten Gutwenger: Einführung in C++ (Teil 2) 10
Statische Methoden
• Methoden, die nicht mit einer Instanz assoziiert sind.– kein Zugriff auf nicht-statische Member
class A {
static int counter;
public:
static void f(int i);
};
int A::counter = 0;
void A::f(int i) {
counter += i;
}
Deklaration
Definition
Carsten Gutwenger: Einführung in C++ (Teil 2) 11
Const-Korrektheit
• Von Konstanten und Const-Referenzen dürfen nur Methoden aufgerufen werden, die mit const deklariert sind.– Diese Methoden dürfen keine Member-Variablen ändern
class Pair { Pair a(1,2);
int x, y; const Pair b(3,4);
public: const Pair &c = a;
Pair(int a, int b)
: x(a), y(a) { } int xa = a.getX(); // ok
int getX() const { int xb = b.getX(); // ok
return x; int xc = c.getX(); // ok
} a.incX(); // ok
void incX() { ++x; } b.incX(); // Fehler!
}; c.incX(); // Fehler!
Carsten Gutwenger: Einführung in C++ (Teil 2) 12
Vererbung
• Klassen und Strukturen können von Basisklassen erben.– Spezifikation der Sichtbarkeit: public, protected, private
– mehrere Basisklassen erlaubt
• Dynamisches Binden bei Methoden– Diese Methoden müssen explizit als virtuell deklariert werden.
– „pure virtual“ Methoden definieren nur Schnittstellen und müssen in abgeleiteten Klassen implementiert werden.
• Klassen mit virtuellen Methoden sollten auch einen virtuellen Destruktor besitzen.
Carsten Gutwenger: Einführung in C++ (Teil 2) 13
Beispiel zur Vererbung
class Figure {
public:
virtual void draw() = 0; // pure virtual
};
class Circle : public Figure {
public:
void draw(); // virtuell, da virtuell in Basisklasse
};
void Circle::draw() {
...
}
Carsten Gutwenger: Einführung in C++ (Teil 2) 14
Überladen von Operatoren
• Operatoren können quasi wie ganz normale Funktionen deklariert werden.– Schreibweise z.B. für Vergleichsoperator: operator==
– Im Prinzip können alle Operatoren überladen werden.
– Operatoren können als Methoden in Klassen oder globale Funktionen definiert werden.
• Beispiel (globale Funktion):
bool operator==(const Pair &a, const Pair &b) {
return (a.getX() == b.getX()) &&
(a.getY() == b.getY());
}
Carsten Gutwenger: Einführung in C++ (Teil 2) 15
Überladen von Operatoren (2)
class Pair {
int x, y;
public:
Pair() : x(0), y(0) { }
Pair(int a, int b) : x(a), y(b) { }
Pair(const Pair &p) : x(p.x), y(p.y) { }
Pair &operator=(const Pair &p);
Pair operator+(const Pair &p);
Pair &operator+=(const Pair &p);
};
ostream &operator<<(ostream &os, const Pair &p);
Carsten Gutwenger: Einführung in C++ (Teil 2) 16
Überladen von Operatoren (3)
Pair & Pair::operator=(const Pair &p) {
x = p.x; y = p.y;
return *this; // gibt Referenz auf aktuelles
} // Element zurück
Pair Pair::operator+(const Pair &p) {
return Pair(x+p.x,y+p.y);
}
Pair &Pair::operator+=(const Pair &p) {
x += p.x;
y += p.y;
return *this;
}
Carsten Gutwenger: Einführung in C++ (Teil 2) 17
Überladen von Operatoren (4)
ostream &operator<<(ostream &os, const Pair &p) {
os << "(" << p.getX() << "," << p.getY() << ")";
return os;
}
• ostream ist Basisklasse für Ausgabestreams– cout ist ein Ausgabestream– ofstream: Klasse für Ausgabe in Dateien
• Warum wird eine ostream-Referenz zurückgegeben?– cout << x << y << z << endl;
– << ist linksassoziativ:((((cout << x) << y) << z) << endl;
Carsten Gutwenger: Einführung in C++ (Teil 2) 18
Generische Programmierung
• Viele Funktionen können für eine Vielzahl von Datentypen definiert werden und sehen nahezu identisch aus:– short abs(short x) { return (x<0) ? -x : x; }
– int abs(int x) { return (x<0) ? -x : x; }
– float abs(float x) { return (x<0) ? -x : x; }
– double abs(double x) { return (x<0) ? -x : x; }
• Geht das nicht einfacher?– template<typename T>
T abs(T x) { return (x<0) ? -x : x; }
– Kann für jeden Datentyp T benutzt werden, der einen Kleiner-Operator, ein unäres Minus, einen Konstruktor für 0 und einen Copy-Konstruktorbesitzt.
Carsten Gutwenger: Einführung in C++ (Teil 2) 19
Generische Klassen
template<typename T1, typename T2>
class Pair {
T1 x;
T2 y;
public:
Pair() { }
Pair(const T1 &a, const T2 &b) : x(a), y(b) { }
Pair(const Pair<T1,T2> &p) : x(p.x), y(p.y) { }
Pair<T1,T2> &operator=(const Pair<T1,T2> &p);
};
Carsten Gutwenger: Einführung in C++ (Teil 2) 20
Generische Klassen (2)
template<typename T1, typename T2>
Pair<T1,T2> &
Pair<T1,T2>::operator=(const Pair<T1,T2> &p)
{
x = p.x;
y = p.y;
return *this;
}
Variablen von diesem Typ definieren:
Pair<int,doube> p(2,1.5);
Carsten Gutwenger: Einführung in C++ (Teil 2) 21
Templates für Fortgeschrittene
• Default-Werte für Template-Parameter
• Werte (z.B. Integer) als Template-Parameter• template<int L, typename T = double>
class Vector {... };
• Spezialisierungen:– Geben für bestimmte Instanziierungen (Werte für Template-
Parameter) eine eigene Implementierung an.
– Sinnvoll, falls Standardimplementierung nicht für jedem Datentyp funktioniert, oder für bestimmte Datentypen eine effizientere Implementierung möglich ist.
Carsten Gutwenger: Einführung in C++ (Teil 2) 22
Pro und Contra Templates
Vorteile:
• Nur eine Codebasis für mehrere Implementierungen, daher leichter zu warten.
• Sehr effizienter Code, da für jeden Datentyp einzeln optimiert.
• Typsicherheit zur Kompilierzeit festellbar.
• Keine gemeinsamen Basisklassen notwendig.
Nachteile:
• Die Größe des erzeugten Codes steigt an.
• Erfordert gutes Fachwissen und Erfahrung, um Programmcode und Kompilermeldungen zu verstehen.
Top Related