12. C++

Ursprünglich als Erweiterung "C with Classes" von Bjarne Stroustrup erstellt, existiert C++ heute als eigenständig standardisierte Sprache, die zwar eine Reihe von Gemeinsamkeiten mit C, jedoch auch eine Reihe von Erweiterungen sowie viele Unterschiede im Detail enthält. Die folgenden Ausführungen sind als grober Überblick gedacht.

Inhalt:

12.1 Begriffe und Idee

Begriffe und Idee der Objektorientierte Programmierung:

12.2 C++-Beispiele zur Einführung

12.2.1 Hello World++

Programm:
	  #include <iostream>

	  using namespace std;

	  int main(void)
	  {
	  	cout << "Hello world!" << endl;
		return 0;
	  }
	  
Compilieren:
	% c++ hello.cc -o hello
	% ./hello

12.2.2 Fahrzeug-Klasse

12.3 C-Beispiel zur Einführung: Bäume

  1. Datenstrukturen:
    typedef int DATA;
    struct bknoten {
            DATA d;
            struct bknoten *l;
            struct bknoten *r;
    };
    struct baum {
            struct bknoten *wurzel;
    };
    
  2. Funktionen:
    struct baum *newBaum(void);
    void printBaum(struct baum *b);
    void printBaum1(struct bknoten *w);
    void insertBaum(struct baum *b, DATA d);
    void insertBaum1(struct bknoten **w, DATA *datap);
    
  3. Hauptprogramm:
    int main(int argc, char *argv[])
    {       
            int i;
            struct baum *t;
    
            t = newBaum(); 
     
            while(!feof(stdin)) {
                    if (fscanf(stdin, "%d", &i) > 0) {
                            insertBaum(t, i);
                    }
            }
            printBaum(t);
     
            return 0;
    }
Das vollständige Programm für mehrere Baeume: baeume.c

12.4 C++ Features anhand von Beispielen

  1. Klassen als "bessere" structs: Definieren Daten und über welche Funktionen auf diese zugegriffen wird
    typedef int DATA;
    
    class bknoten {
        public:
    	/* Oeffentliche Attribute */
            DATA d;
            class bknoten *l;
            class bknoten *r;
            
    	/* Oeffentliche Methoden */
            bknoten(DATA d2);
            void print(void);
    };
    
    class baum {
    	/* Private Attribute */
            class bknoten *wurzel;
    
    	/* Private Methoden */
            void print1(class bknoten *k);
            void insert1(class bknoten **k, DATA *datap);
    
        public:
    	/* Oeffentliche Methoden */
            baum(void);
            void print(void);
            void insert(DATA data);
    };
    
  2. Konstruktoren und Destruktoren: Initialisieren und zerstören Daten bzw. geben diese frei
    /* Konstruktor bknoten Klasse */
    bknoten::bknoten(DATA *d2)
    {
            d = *d2;
            l = NULL;
            r = NULL;
    }
    
    /* Konstruktor baum Klasse */
    baum::baum(void)
    {
            wurzel = NULL;
    }
    
    /* Destruktor baum Klasse */
    baum::~baum(void)  
    {
            printf("Baum freigeben... (Not Implemented Here)\n");
    }
    
  3. Dynamische Speicherverwaltung: die Operatoren new und delete sind wie die Funktionen malloc() und free(), rufen jedoch zusätzlich automatisch Konstruktor bzw. Destruktor auf
    int main(int argc, char *argv[])
    {                       
            int i;  
            class baum *t;
           
            t = new baum();     /* malloc() + Konstruktor */
    
            while(!feof(stdin)) { 
                    if (fscanf(stdin, "%d", &i) > 0) {
                            t->insert(i);
                    }
            } 
            t->print();
     
            delete t;           /* Destruktor + free() */
    
            return 0;
    }
  4. Member-Funktions: inline & "normal"

    Genau wie bei structs können Klassen in Header-Files deklariert werden die in allen Modulen (Dateien) benutzt werden, während Funktionen nur in einem Modul definiert werden. Inline Funktionen sind da bekannt wo die Klasse bekannt ist und werden an Ort und Stelle eingesetzt. Ergebnis: Schneller da kein Funktionsaufruf, aber größeres Programm durch Code-Kopien.

  5. Datenschutz: public, protected, private, friend
    class bknoten {
    private:
            DATA d;
    
    public:
            class bknoten *l;
            class bknoten *r;
            bknoten(DATA d2);
            void print(void);
            
            set(DATA d2) { d = d2; };
            DATA get(void){ return d; }
    
            friend void baum::insert1(class bknoten **w, DATA *datap);      
    };
    
    void baum::insert1(class bknoten **w, DATA *datap)
    { ...
            } else {
                    if (*datap < (*w)->get()) {
                    ...
    }
    
Das vollständige Programm für mehrere Bäume in C++: baeumePP.cc

12.5 Weitere Features

  1. Vererbung: Weitergabe von Attributen (Methoden und Daten) an weitere Klassen

    1. Von Klasse "Fahrzeug" (siehe oben) abgeleitete Klasse "Auto": Ein Auto ist ein Fahrzeug mit einer Anzahl Rädern

      class Auto : public Fahrzeug {
      public:
              int raeder;       // Anzahl
              
              Auto(char *f, int r) : Fahrzeug(f) { raeder = r; };
              void print(){
                      printf("Auto raeder=%d, farbe=%s; ", raeder, farbe);
                      Fahrzeug::print();
              }
      };
      
    2. Anwendung:
      int main(void)
      {
              Fahrzeug f("schwarz");
              f.print();
      
              Auto a("rot", 4);
              a.print();
      
              return 0;
      }
      
    3. Beispielausgabe:
      Fahrzeug farbe=schwarz
      Auto raeder=4, farbe=rot; Fahrzeug farbe=rot 
  2. Überladen von Operatoren:
  3. Polymorphie: Gleicher Funktionsname mit unterschiedlichen Parametern
    int    max(int a,    int b)   { return (a>b)?a:b; }
    double max(double a, double b){ return (a>b)?a:b; }
    
    int main(void)
    {
            printf("1) max = %d\n", max(2,1));
            printf("2) max = %f\n", max(0.2,-1.0));
            return 0;
    }
    
    Ausgabe:

    1) max = 2
    2) max = 0.200000
    
  4. Vorgaben für Funktionsparameter:
    class Auto : Fahrzeug {
            ...
            Auto(char *f="rot", int r=4)
                    : Fahrzeug(f) { raeder = r; };
            ...
    
    main()
    {
            ...
            Auto rotes_auto;                rotes_auto.print();
            Auto blaues_auto("blau");       blaues_auto.print();
            Auto blaues_zweirad("blau", 2); blaues_zweirad.print();
    
    Ausgabe:
    Auto raeder=4, farbe=rot; Fahrzeug farbe=rot
    Auto raeder=4, farbe=blau; Fahrzeug farbe=blau
    Auto raeder=2, farbe=blau; Fahrzeug farbe=blau
    
  5. Referenzen: Verändern von Funktionsargumenten ohne Zeiger
    void swap_C(int *a, int *b) {
            int c = *b; *b = *a; *a = c;
    }
    
    void swap_CPlusPlus(int &a, int &b)
    {
            int c = b; b = a; a = c;         // keine *
    }
    
    int main(void)
    {
            int a=1, b=2;
    
            printf("a=%d, b=%d\n", a, b);
            swap_C(&a, &b);
            printf("a=%d, b=%d\n", a, b);
            swap_CPlusPlus(a, b);            // keine &
            printf("a=%d, b=%d\n", a, b);
            
            return 0;
    }
    
    
  6. IO Streams: Objektorientierte Ein-/Ausgabe-Bibliothek. Mit iostreams koennen eigene Datentypen genau wie eingebaute mittels << ausgegeben werden, wenn die dazu noetigen Funktionen bereitgestellt werden:

    #include <iostream>       // kein .h
    
    using namespace std;           // gcc 3.x und spaeter, siehe unten
    
    class Complex {
    public:
            int r, i;
            
            Complex(int r2, int i2){ r=r2; i=i2; }
    
            friend ostream& operator <<(ostream &os, Complex &obj)
            {
                    os << "(" << obj.r << "+" << obj.i << "i)";
                    return os;
            }
    };
    
    int main(void)
    {
            int a=2, b=3;
            Complex z(3,4);
    
            cout << a << " + " << b << " = " << a+b << endl;
    
            cout << "a = " << a << endl;
            cout << "b = " << b << endl;
            cout << "z = " << z << endl;
            return 0;
    }
    
    Ausgabe:
    2 + 3 = 5
    a = 2
    b = 3
    z = (3+4i) 
  7. Exceptions: Abfangen von fehlern mit try, catch, throw

    void func()
    {
      try
      {
        throw 1;
      }
      catch(int a)
      {
        cout << "Caught exception number:  " << a << endl;
        return;
      }
      cout << "No exception detected!" << endl;
      return;
    } 
    aus: "C++ Exception Handling" von Denton Woods, Stand 10.1.2007

  8. Namespaces: Vermeidung von Namenskonflikten

    Problem:

    Loesung: aus: "C++ Namespaces" von Glen McCluskey & Associates, Stand 10.1.2007

  9. Templates: Erstellen allgemeingueltiger ("generischer") Datentypen

    Beispiel: Baumklasse fuer beliebige Objekte

    Vollständiger Quellcode: baeumePPT.cc

  10. Standard Template Library: Sammlung von Klassentemplates, Grundlage der C++ Standard Library

    Inhalt: Klassen und Templates für ...

    Verweise:


(c) Copyright 2007 Hubert Feyrer <hubert@feyrer.de>
$Id: 12-cplusplus.html,v 1.10 2008/02/15 12:04:37 feyrer Exp $