next up previous contents index
Next: Sonstiges Up: Grundtypen und abgeleitete Typen Previous: Abgeleitete Typen

Referenzen

    Eine Referenz ist ein alternativer Name für ein Objekt:

    int i = 42;
    int& r = i; // r und i stehen fuer Dasselbe.
    int x = r;  // x = 42;
    r = r - 1;  // i = 41;

Der Wert einer Referenz kann nicht verändert werden

tex2html_wrap_inline5173 eine Referenz muß initialisiert werden.

Der Initialisierer einer Referenz muß ein L-Wert (ein zuweisbares Objekt) sein:  

    double &r = 3.14;  // FALSCH: kein L-Wert

Ausnahme: Referenzen auf Konstante:

    const double &r = 3.14;  // OK.

entspricht der Sequenz

    double temp = 3.14;
    double &r = temp;

Referenzen als Parameter: Call-by-Reference    

Funktionen, die ihre Parameter verändern:

   void inc(int& i)
   { i++;}
   int main()
   { int x = 1;
     inc(x);   // x = 2;
   }

Zeitersparnis bei der Parameterübergabe:

    class bank
    { kunden viele[10000];
      ...
    };
    void pruefe(bank b);

Diese Parameterübergabe benutzt den Copy-Konstruktor.  

Besser:

    // ein Referenzargument, 
    // das nicht veraendert wird:
    void pruefe(const bank &b);

Referenzen als Funktionsergebnis

Ebenfalls zur Vermeidung von Kopien:

    bank& zahlezinsen()
    { ...
    }

Vorsicht: Solche Funktionen können links von Wertzuweisungen stehen:

    int& index(int *ip, int i)
    { return ip[i];
    }
    int a[] = {1,2,3};
    int main()
    { index(a,1) = 17; // a = {1,17,3};
    }

Wenn dies nicht beabsichtigt ist:

    const int& index(int *ip, int i)
    { return ip[i];
    }

Vorsicht: niemals Referenzen auf lokale Objekte zurückgeben:

     string& anhaengen(string& s1, string& s2)
     { string tmp = s1;
       tmp += s2; 
       return tmp;
     }

Solche Objekte sind nach Verlassen der Funktion nicht mehr da!

Funktionen

     Eine Funktionsdeklaration enthält

   int strlen(char *);
   void printdate(date);
   void stop(void);

Als Hilfe für den Leser darf die Funktionsdeklaration Parameternamen enthalten:   

    int inset(myset menge, int suchmich);

Jede verwendete Funktion muß irgendwo (genau einmal) definiert werden. Eine Funktions-Definition ist eine Funktions-Deklaration mit Rumpf:   

   void swap(int*, int*);
   ...
   void swap(int *p, int *q)
   { int tmp = *p;
     *p = *q;
     *q = tmp;
   }

Alle Parameter, die im Rumpf benutzt werden, müssen einen Namen haben. tex2html_wrap_inline5173 Alle die nicht benutzt werden, brauchen keinen Namen.  

Inline-Funktionen   

Wie Elementfunktionen und Konstruktoren dürfen Funktionen auch inline definiert werden:

    inline void printexpr(int val)
    { cout << "(" << val << ")";

Inline ist eleganter und sicherer als cpp-Makros:

    #define mul(x,y) (x*y)
    ...
    cout << mul(6+1,4)    // SURPRISE!

Parameterübergabe

  Beim Aufruf werden die formalen Wert-Parameter mit den aktuellen Parametern initialisiert ( tex2html_wrap_inline5151 Copy-Konstruktor). Die Typen werden abgeglichen durch standardmäßige und Benutzer-definierte Typumwandlungen.

Die formalen Referenz-Parameter werden zu Referenzen auf die aktuellen Parameter.

Call-by-Value tex2html_wrap_inline5151 tex2html_wrap_inline5153 Call-by-Reference    

    void f(int i, int& k)
    { i++;  // veraendert lokales i
      k++;  // veraendert den akt. Parameter
    }

Call-by-Reference wegen der möglichen Seiteneffekte vermeiden. Wenn wegen Effizienz eingesetzt: verwende const-Argumente:

    void f(const large& arg)
    { ...
    }

Referenzübergabe für

ist nur an formale Parameter der Art const T& möglich:

    float sqrt(const float&);  // Wurzel
    int main()
    { float r;
      double d;
      r = sqrt(2.0f); // Ref. auf temp=2.0f
      r = sqrt(r);    // Ref. auf r
      r = sqrt(d);    // Ref. auf temp=(float)d

Funktionsergebnisse

  Funktionen, die nicht als void deklariert sind, müssen ein Funktionsergebnis liefern.

    int fac(int n)
    { if (n==0)
      return 1;
      else return n*fac(n-1);
    }

Die Semantik der Ergebnisrückgabe entspricht (wie die der Argumentübergabe) der der Initialisierung.

  

Der Typ des return-Ausdrucks und der Typ der Funktion werden abgeglichen durch standardmäßige und Benutzer-definierte Typumwandlungen.  

Default-Argumente

  Funktionen und Konstruktoren dürfen Default-Argumente haben, die beim Aufruf weggelassen werden können:

   void printdate(date d, int kurz=1);
   { cout << d.day << ".";
     if (kurz)
        cout << d.month << ".";
     else cout << monthname(d.month) << ".";
     cout << d.year << ".";
   }
   ... 
   printdate(date(17.5.1993),1); // kurz
   printdate(date(17.5.1993));   // dasselbe
   printdate(date(17.5.1993),0); // lang

Default-Argumente sind nur für die ``hinteren'' Funktionsparameter möglich.

    int print(int basis = 10, int value); // FEHLER!

Vorsicht vor Syntaxfehlern:

    void sonicht(char*=0);  // Syntax-Fehler!
                 // *= ist Zuweisungsoperator

Beliebig viele Argumente

    Wenn Anzahl und Typen der Argumente nicht exakt angegeben werden können, werden Auslassungspunkte ... als letztes Element der Parameterliste benutzt:

    int printf(const char*, ...);

Die Funktion muß Informationen über Anzahl und Art der Parameter erhalten. Im Falle von printf geschieht dies über den 1. Parameter (Format-String):

     printf("Hello Number %d\n",7);

Der Compiler kann keine Typen prüfen und anpassen (char und short werden zu int, float wird zu double).

In <stdarg.h> stehen Zugriffsmakros für nicht-spezifizierte Parameter zur Verfügung.

Ein gut entworfenes Progamm sollte solche Funktionen jedoch möglichst nicht benutzen.

Zeiger auf Funktionen

  Es gibt zwei zulässige Operationen auf Funktionen:

Der aus der Adressbestimmung resultierende Zeiger kann zum Aufruf der Funktion benutzt werden:

    void error(char *p);
    void (*fuptr)(char*) = &error;
    ...
    (*fuptr)("Du Pfeife"); // ruft error() auf
    fuptr("Selber"); // dto. mit impl. Konvertierung

Die Klammern sind nötig, da () höhere Priorität hat als *.

Die Deklaration des Zeigers enthält Argumentliste und Ergebnistyp. Diese müssen mit der Signatur der Funktion exakt übereinstimmen:

 

    void (*fuptr)(char*);
    int f1(char*);
    void f2(int*);
    ...
    fuptr = &f1;   // FEHLER: Ergebnistyp
    fuptr = &f2    // FEHLER: Parametertyp


next up previous contents index
Next: Sonstiges Up: Grundtypen und abgeleitete Typen Previous: Abgeleitete Typen

Peter Pfahler, 1997