next up previous contents index
Next: Überladen von Funktionen und Up: Anweisungen und Ausdrücke Previous: Anweisungen

Ausdrücke

 

Ein Ausdruck ist eine Folge von Operatoren und Operanden. Er kann einen Wert liefern und Seiteneffekte verursachen.

   

Im Allgemeinen ist die Reihenfolge, in der Operanden ausgewertet werden, undefiniert:

   k = i + f(i++);  // Wert von k undef.
   i = v[i++];      // Wert von i undef.

Die Bedeutung eines Ausdrucks ergibt sich aus den Regeln für

   

*f(i) bedeutet *(f(i)), da () höhere Präzendenz hat als *. Es ist also die Dereferenzierung eines Funktionsergebnisses.
c=x!=0 bedeutet c = (x != 0), da die Vergleichsoperationen höhere Präzedenz haben als die Wertzuweisung.
a=b=c=0 heißt a = (b = (c = 0)), da die Wertzuweisung rechtsassoziativ ist.
a-b-c bedeutet (a - b) - c, da die Subtraktion linksassoziativ ist.

Übersicht über die Operatoren

 

Op Bedeutung Typ Assoz. Präzedenz
, Komma-Operator bin links 1
= Wertzuweisung bin rechts 2
+= Wertzuweisung bin rechts 2
-= Wertzuweisung bin rechts 2
*= Wertzuweisung bin rechts 2
/= Wertzuweisung bin rechts 2
|= Wertzuweisung bin rechts 2
^= Wertzuweisung bin rechts 2
&= Wertzuweisung bin rechts 2
%= Wertzuweisung bin rechts 2
<<= Wertzuweisung bin rechts 2
>>= Wertzuweisung bin rechts 2
? : Bedingung tern rechts 3
|| Log. Oder bin links 4
&& Log. Und bin links 5
| Bitweises Oder bin links 6
^ Bitweises XOR bin links 7
& Bitweises Und bin links 8
== Gleichheit bin links 9
!= Ungleichheit bin links 9
< Kleiner bin links 10
<= Kleiner gleich bin links 10
> Größer bin links 10
>= Größer gleich bin links 10
<< Links-Shift bin links 11
>> Rechts-Shift bin links 11

Op Bedeutung Typ Assoz. Präzedenz
+ Addition bin links 12
- Subtraktion bin links 12
* Multiplikation bin links 13
/ Division bin links 13
% Modulo bin links 13
->* Member-Zeiger bin links 14
.* Member-Zeiger bin links 14
++ Pre-Inkrement un rechts 15
-- Pre-Dekrement un rechts 15
* Dereferenzierung un rechts 15
& Adresse un rechts 15
+ Unäres Plus un rechts 15
- Unäres Minus un rechts 15
! Logisches Not un rechts 15
~ Bitweises Not un rechts 15
++ Post-Inkrement un rechts 15
-- Post-Dekrement un rechts 15
(type) Cast bin rechts 15
new Allokation un/bin rechts 15
delete Deallokation un/bin rechts 15
[] Indizierung bin links 16
() Funktionsaufruf bin links 16
() funktionaler Cast bin links 16
. Selektion bin links 16
-> Zeigerselektion bin links 16
sizeof Größe un rechts 16
:: Scope Resolution un/bin links 17

Einige ausgewählte Operatoren

:: Scope Resolution    

Es gibt vier Arten von Gültigkeitsbereichen:  

Der binäre :: Operator erlaubt Zugriff auf verdeckte Klassenelemente:

    class X
    { public: 
        static int xval;
        int getval();
    };
    X::xval = 0;
    int X::getval() {return xval;}

Der unäre :: Operator erlaubt Zugriff auf Namen im File-Gültigkeitsbereich, selbst wenn sie verdeckt sind:

    int g = 0;
    int f(int g)
    { if (g)            // lokales g
      return g;
      else return ::g;  // globales g
    }

sizeof: Größenbestimmung  

liefert die Größe in Bytes seines Operanden. Der Operand ist:

sizeof ist nicht anwendbar auf Funktionen, Bitfelder, void und Arrays ohne Dimensionsangabe.

Auf eine Referenz angewendet, liefert sizeof die Größe des Objekts, auf das die Referenz verweist:  

    class X
    { ...
    };
    X obj;
    X& ref = obj;
    const int i = sizeof ref; // == sizeof(X)

Für Arrays liefert sizeof die Gesamtzahl der Bytes im Array:  

    int arr[100];
    const int i = sizeof arr;

Beispiel:

int i=99;
double d = 2.0;

int main()
{ cout << "int: " << sizeof(int) << endl;
  cout << "i: " << sizeof i << endl;
  cout << "i++: " << sizeof i++ << endl;
  cout << "d=d+i: " << sizeof(d=d+i) << endl;
  cout << "wert(i): " << i << endl;
  cout << "wert(d): " << d << endl;
  return 0;
}

liefert

    int: 4
    i: 4
    i++: 4
    d=d+i: 8
    wert(i): 99
    wert(d): 2

new: Speicherallokation   

Der Operator new bewirkt

1.
Allokation freien Speicherplatzes für das Objekt.
2.
Initialisierung des Objektes.
3.
Rückgabe eines geeigneten Zeigers auf das Objekt.

    date *d;
    d = new date;

ruft den Default-Konstruktor von date auf.

    date *d;
    d = new date(5,6,1993);

ruft den date(d,m,y)-Konstruktor auf.

Arrays werden wie folgt allokiert:  

    char *p = new char[n];
    date *urlaub = new date[29];

Dabei wird der Default-Konstruktor für jedes Array-Element aufgerufen. Dynamisch allokierte Arrays können nicht explizit initialisert werden!  

    class elem
    { int i;
    public:
     elem():      i(0) { cout <<i<<' ';}
     elem(int v): i(v) {cout <<i<<' ';}
     ~elem()           { cout <<'~'<<i<<' ';}
    };
    
    elem e1;
    elem e2(1);
    elem e3[2];
    elem e4[2] = {elem(2), elem(3)};
    
    elem *p1 = new elem;
    elem *p2 = new elem(4);
    elem *p3 = new elem[2];
    // Initialisierte Arrays gehn nicht!

erzeugt folgende Ausgabe:

    0 1 0 0 2 3 0 4 0 0 ~3 ~2 ~0 ~0 ~1 ~0

Man sieht: Dynamisch allokierte Objekte werden nicht automatisch zerstört und deallokiert.

Die Initialisierung wird nur bei erfolgreicher Allokation (Rückgabewert von new ungleich 0) ausgeführt.  

tex2html_wrap5231

    elem *p = new elem[n];
    if (!p)
    { cerr << "New failed (elem[n]) \n";
      exit 1;
    }

Beispiel:

#include <iostream.h>
#include <stdlib.h>

class vector
{   double *co;
    int dim;
  public: 
    vector(int d);
    double& operator [] (int d);
};

vector::vector(int d) : dim(d)
{ co = new double[dim];
  if (!co)
  { cerr << "New failed\n";
    exit (1);
  }
}

double& vector::operator[] (int d)
{ if (d >= 0 && d < dim)
    return co[d];
  else { cerr << "index out of range\n";
         exit(1);
       }
}

delete: Speicher-Recycling    

Der Operator delete bewirkt

1.
Aufruf des Destruktors für das Objekt.
2.
Freigabe des Speichers für das Objekt.

    date *d = new date;
    ...
    delete d;  // Zerstoeren und Freigeben

delete darf nur auf Zeiger angewandt werden, die durch new erzeugt wurden. Sonst ist das Verhalten undefiniert. delete auf den Nullpointer angewandt ist harmlos.

    int i;
    int *p = &i;
    delete p;  // FEHLER !!!
    p = new int[100];
    p = p + 1;
    delete p;  // FEHLER !!!

Arrays werden mit delete [] recycled. Diese Form führt dazu, daß der Destruktor für jedes Element des Arrays aufgerufen wird:

    class elem
    { int i;
    public:
      elem() : i(0)      {cout <<i<<' ';}
      ~elem()            {cout <<'~'<<i<<' ';}
    };
    
    int main()
    { elem *p3 = new elem[6];
      delete [] p3;
    }

liefert:

    0 0 0 0 0 0 ~0 ~0 ~0 ~0 ~0 ~0

Beispiel:

// Ein dynamisch wachsender Keller
const int stksize = 5;
const int stkgrow = 5;

class stack
{ 
private:
  int *stkref;
  int *stkend;
  int *stkptr;
public:
  stack(int size = stksize);
  ~stack()               {delete [] stkref;}
  int push(int elem);
  int pop()              { return *--stkptr;}
  int empty() const      { return stkref == stkptr;}
};
stack::stack(int size)
{ stkref = new int[size];
  if (!stkref)
    { cerr << "No more Memory\n";
      exit(2);
    }
  stkend = stkref+size;
  stkptr = stkref;
}

int stack::push(int elem)
{ if (stkptr >= stkend)
    { int *snew = new int[stkend-stkref+stkgrow];
      for (int *p = stkref; p < stkend; p++)
        snew[p-stkref] = *p;
      stkend = snew + (stkend-stkref)+stkgrow;
      stkptr = snew + (stkptr-stkref);
      delete [] stkref;
      stkref = snew;
    }
  return *stkptr++ = elem;
}

.* und ->*: Zeiger auf Klassenelemente      

Funktionen können über Zeiger indirekt aufgerufen werden:

    void print(char *s)
    { cout << s << endl;
    } 
    void (*fp)(char*) = &print;
    (*fp)("hallo");

Dies ist auch für Elementfunktionen möglich. Dazu muß der Klassennamen angegeben werden:

    class X
    { ....
      public:
      void print (char*s);
    };
    void (X::*fp)(char*) = &X::print;

Zum indirekten Aufruf brauchen wir ein Objekt der Klasse X:  

    X a;
    (a.*fp)("string1");     // Operator .*
    X *b;
    (b->*fp)("string2");    // Operator ->*

Zeiger auf public Datenelemente von Klassen sind auch möglich:

     struct A
     { int a;
       int b;
     };
     int A::*iptr = &A::a;
     A var;
     var.*iptr = 12;
     A *ptr;
     A::*iptr = &A::b;
     ptr->*iptr = 13;


next up previous contents index
Next: Überladen von Funktionen und Up: Anweisungen und Ausdrücke Previous: Anweisungen

Peter Pfahler, 1997