mutable

Ein Const-Objekt darf in sich nicht verändert werden. Allerdings bietet C++ hier dem Benutzer, zwischen dem Wert eines Objektes zu unterscheiden und zwischen Daten, die den Wert des Objektes nicht betreffen.

Als Beispiel sei folgendes Problem genannt: Eine Klasse besitzt ein Datum und den Entwickler interessiert, wie oft dieses Datum gelesen wird. Nun können auch Const-Objekte gelesen werden. Der einfachste Weg wäre nun, im Objekt zu speichern, wie oft der Wert abgefragt wurde. Im Falle eines Const-Objektes muss dafür eine Variable definiert werden, die verändert werden darf, obwohl das Objekt eigentlich konstant ist. Hierfür wird das Schlüsselwort mutable verwendet:

class Date
{
private:
  int Value;
  mutable int ReadCounter;
 
public:
  Date( int value )
    : Value( value ), ReadCounter( 0 ) 
  {
  }  
 
  int GetValue() const
  {
    ReadCounter++; 
    return Value;
  }
 
  int GetReadCounter() const 
  {
    return ReadCounter;
  }       
};

Wichtig ist hier, dass der ReadCounter den Wert des Objektes nicht verändert. Was hier zum Wert eines Objektes gehört und was nur Zusatzinformationen sind, erklärt der Entwickler indirekt ebenfalls durch mutable.

Ein Beispiel für Listen

Das vorherige Beispiel ist durchaus realistisch, aber nicht sonderlich sinnvoll. Dennoch möchte ich ein sinnvolles Beispiel geben und dafür schauen wir uns den Index-Zugriff einer doppeltverketteten Liste an:

class List
{
  int    Count;
  Node * First;
  Node * Last;
 
  Node const * operator [] ( int index ) const
  {
    int i = 0;
    Node * result = First; 
 
    while( i < Count && i < index )
    {
      i++;
      result = result->Next; 
    }   
 
    return result; 
  }
 
  int Add( Node * );   // nicht const 
};

Nun wollen wir mit der Liste ein Array ersetzen, dass häufig von Anfang bis Ende durchlaufen wird. Je weiter man an das Ende der Liste indiziert, desto langsamer wird der Zugriff, da diese Implementation immer vom ersten Element losrennt und sich durch alle Elemente bis zum gesuchten durchhangelt.

Da wir wissen, dass wir die Liste in der Regel von Vorne nach Hinten durchwandern, können wir die Liste beschleunigen, in dem wir uns die letzte Position merken. Das funktioniert ebenfalls für eine Liste, die ein Const-Objekt ist:

class List
{
  int    Count;
  Node * First;
  Node * Last;
 
  mutable Node * LastSelected;
  mutable int    LastIndex;  
 
  Node const * operator [] ( int index ) const
  {
    int i;
    Node * result;   
 
    if( index < Lastindex )
    {  
      i = 0; 
      result = First; 
    } 
    else
    {
      i = LastIndex 
      result = LastSelected; 
    }   
 
    while( i < Count && i < index )
    {
      i++;
      result = result->Next; 
    }   
 
    LastSelected = result;
    LastIndex = index; 
 
    return result; 
  }
 
  int Add( Node * );   // nicht const 
};

Diese beginnt nur vorne, wenn der gesuchte Index kleiner ist der vorherige Index ist, ansonsten wird von der Stelle an weitergesucht, an der die letzte Suche beendet wurde. Das durchlaufen der Liste über den Index ist damit deutlich beschleunigt.

Aber auch her ändert sich der Wert der Liste nicht - es können keine Elemente hinzugefügt werden oder vertauscht werden. Als mutable werden nur Zusatzinformationen bezeichnet, die für den Wert der Liste unbedeutend sind.