Wenn man den Operator []

überlädt, dann sollte die zugehörige Funktion eine Referenz auf eine Variable liefern. Würde die Funktion nur einen Wert liefern, dann wäre der resultierende Ausdruck bei der Verwendung des []-Operators kein lvalue, an den etwas zugewiesen werden darf. Ein Ausdruck der Form objekt[1] = ... wäre dann nicht möglich.

Dasselbe gilt für andere Operatoren, wenn man deren Ergebnis möglicherweise als lvalue verwenden möchte.

Dazu ein kleines Beispiel, mit dem das Überladen des []-Operators gezeigt wird:

// Time-stamp: "(15.11.01 16:10) intarr.cpp [Klaus Wachtler (aw38)]"
//
// demonstriert eine Klasse für int-Felder
// mit dynamischer Allokierung:
// Bei jedem Zugriff hinter das bislang allokierte
// Feld wird automatisch neu allokiert.

#include <iostream>
#include <cstdlib>

using namespace std;

class intarr
{
private:

  int       *p;  // Zeiger auf das eigentliche Feld
  size_t     l;  // die allokierte Länge

  void resize( size_t index );

public:
  intarr( void );
  ~intarr( void );
  int &operator[]( long index );

};

// Der Konstruktor sorgt für jedes intarr-Objekt für
// die Initialisierung (anfangs 0 Feldelemente).
intarr::intarr( void )
{
  p = NULL;
  l = 0;
}

// Der Destruktor sorgt für jedes intarr-Objekt für
// die Freigabe des allokierten Feldes.
intarr::~intarr( void )
{
  if( (p) )     // wurde überhaupt schon etwas allokiert?
    {
      free( p );
    }
}

// Der Operator [] wird mit einer Funktion überladen, die:
// a) ein Unterschreiten des Feldes (index<0) abfängt, und
// b) das Feld mit intarr::resize() ggf. verlängert, und dann
// c) eine Referenz auf das index'te Element des Objekts liefert.
int &intarr::operator[]( long index )
{
  if( (index<0) )          // Unterschreitung abfangen
    {
      cerr << "So geht das nicht!" << endl;
      cerr << "intarr::operator[]( long index ): (index=="
           << index << "), es muss aber (index>=0) sein!" << endl;
      exit( 1 );
    }

  resize( index );         // Das Feld ggf. verlängern

  return p[index];         // weil diese Funktion als Referenz
  // vereinbart ist (int &), wird hier
  // nicht der Wert eines Feldelements
  // geliefert, sondern eine Referenz darauf.
}

// resize() sorgt dafür, dass ein Feldelement mit der Nummer (index)
// verwendet werden kann.
// Wenn index ausserhalb des allokierten Bereichs liegt,
// wird das Feld mit realloc() entsprechend verlängert.
// Der neue Bereich wird mit (0)en vorbesetzt.
void intarr::resize( size_t index )
{
  if( (index >= l) )
    {
      // der gewünschte Index liegt hinter der
      // momentanen Feldgrenze, also muss das Feld
      // vergrössert werden:
      p = (int *)realloc( p, (++index)*sizeof(*p) );

      // die neugewonnenen Elemente erstmal auf 0 setzen:
      while( l<index )
        {
          p[l++] = 0;
        }
    }
}

// Testprogramm:
// Ein Feld (ohne Angabe von Grenzen!) wird als Variable feld
// vereinbart. Dann werden einige Elemente ( feld[4], feld[3] )
// belegt und feld[0] bis feld[5] dann ausgegeben.
// Zuletzt wird durch einen negativen Index ein Unterlauf des
// Feldes und damit ein Programmabbruch provoziert.

int main( int nargs, char *args[] )
{
  intarr     feld;   // Ein Objekt der Klasse wird erzeugt
  // (ohne Elemente)

  feld[4] = 44;      // feld[0] bis feld[4] werden allokiert,
  // feld[4] wird mit 44 beschrieben, der Rest mit 0
  feld[3] = 3;       // feld[3] wird überschrieben

  for( int i=0; i<=5; i++ )
    {
      // feld[0] bis feld[4] werden ausgegeben,
      // dann wird feld[5] mit realloc() geschaffen, mit 0
      // vorbesetzt und auch ausgegeben:
      cout << "feld[" << i << "] = " << feld[i] << endl;
    }

  feld[-5] = 12; // erzeugt Programmabbruch mit exit()

  return 0;
}

Die zugehörige Ausgabe lautet:

feld[0] = 0
feld[1] = 0
feld[2] = 0
feld[3] = 3
feld[4] = 44
feld[5] = 0
So geht das nicht!
intarr::operator[]( long index ): (index==-5), es muss aber (index>=0) sein!



AnyWare@Wachtler.de