HOWTO · C++

Überladen Sie den Klammeroperator in C++

Erfahren Sie, wie Sie den C++-Operator[] mit veränderbarem und const-Zugriff, optionalen Indexprüfungen und einem Beispiel für eine benutzerdefinierte Datenstruktur überladen.

Auf dieser Seite

Um operator[] in C++ zu überladen, deklarieren Sie ihn als Elementfunktion, die einen Index entgegennimmt. Geben Sie eine Referenz zurück, wenn Aufrufer über object[index] einen Wert zuweisen können sollen; stellen Sie für schreibgeschützte Objekte eine const-Überladung bereit. So kann ein benutzerdefinierter Typ einen arrayähnlichen Zugriff anbieten und zugleich seine Speicher- und Zugriffsregeln selbst festlegen.

Warum und wann operator[] überladen?

Durch Operatorüberladung wird festgelegt, wie ein Operator mit einem benutzerdefinierten Typ, etwa einer Klasse, arbeitet. Dadurch lassen sich Container oder Matrizen intuitiv verwenden: Aufrufer können items[index] schreiben, statt eine separate Zugriffsfunktion aufzurufen. Eine Klassenüberladung wirkt sich nur auf Ausdrücke mit dieser Klasse aus. Sie kann nicht ändern, wie [] mit ausschließlich eingebauten Typen funktioniert.

Die Überladung ist eine Elementfunktion namens operator[]. Sie muss im Abschnitt public stehen, wenn Aufrufer sie verwenden sollen. Der Parametertyp kann zum Indexierungsmodell passen, zum Beispiel std::size_t für einen nichtnegativen Containerindex. Der Rückgabetyp bestimmt, was Aufrufer mit dem ausgewählten Element tun können.

operator[] zum Lesen und Schreiben implementieren

Dieses vollständige Beispiel speichert drei initialisierte Ganzzahlen. Die veränderbare Überladung gibt int& zurück, sodass eine Zuweisung das ausgewählte Element aktualisiert. Die const-Überladung gibt const int& zurück; dadurch kann das Element beim Lesen über ein const-Objekt nicht verändert werden.

#include <array>
#include <cstddef>
#include <iostream>

class IntList {
private:
    std::array<int, 3> values{10, 20, 30};

public:
    int& operator[](std::size_t index) {
        return values[index];
    }

    const int& operator[](std::size_t index) const {
        return values[index];
    }
};

int main() {
    IntList items;
    items[1] = 25;

    const IntList& view = items;
    std::cout << items[1] << ' ' << view[2] << '\n';
}
25 30

Das Programm gibt 25 30 aus. Im ersten Ausdruck wird die nicht-const-Überladung ausgewählt; ihre Referenz ermöglicht die Zuweisung. Für das const-Objekt wird die const-Überladung ausgewählt. Würde das Element als Wert zurückgegeben, wäre zwar ein Lesezugriff möglich, doch eine Zuweisung würde nur den temporären Wert ändern und nicht das gespeicherte Element.

const-Zugriff unterstützen und ungültige Indizes behandeln

Das obige Beispiel verwendet std::array::operator[], das einen Index innerhalb des gültigen Arraybereichs voraussetzt. Ein ungeprüfter Indexzugriff führt keine Bereichsprüfung durch; die Verwendung eines ungültigen Index hat undefiniertes Verhalten. Dasselbe gilt, wenn eine benutzerdefinierte Überladung direkt auf ein ungeprüftes zugrunde liegendes Array oder einen Container zugreift.

Soll die Klasse Indizes prüfen, fügen Sie vor dem Zugriff auf den Speicher eine ausdrückliche Prüfung hinzu und lösen Sie std::out_of_range aus, wenn der Index außerhalb des unterstützten Bereichs liegt. Binden Sie für diesen Typ <stdexcept> ein. Das ist eine Entwurfsentscheidung für die Klasse und kein Verhalten, das operator[] automatisch bereitstellt. Bei einem Container, der der üblichen Aufteilung der Standardbibliothek folgt, kann operator[] ungeprüft bleiben, während eine separate geprüfte Zugriffsfunktion die Validierung übernimmt.

operator[] auf eine benutzerdefinierte Datenstruktur anwenden

Eine Matrix kann den Zugriff matrix[row][column] unterstützen, indem ihr erstes operator[] ein zeilenähnliches Objekt zurückgibt, zum Beispiel std::vector<int>&. Das zweite [] gehört dann zu diesem Zeilenobjekt. Für eine const-Matrix stellen Sie eine const-Überladung bereit, die const std::vector<int>& zurückgibt, damit die Zeile und ihre Elemente schreibgeschützt bleiben.

Jeder Schritt einer verketteten Indexierung muss einen Wert zurückgeben, der den nächsten []-Zugriff unterstützt. Die Zeilenreferenz ermöglicht außerdem, dass ein Ausdruck wie matrix[row][column] = value das gespeicherte Element erreicht. Die Matrixklasse sollte ihre eigenen Regeln für Indizes festlegen: Bei ungeprüftem Zugriff auf Zeilen und Spalten müssen die Indizes gültig sein; ausdrückliche Prüfungen lassen sich bei jedem der beiden Indexierungsschritte ergänzen.