Mutable-Schlüsselwort
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 30 von 104.
Sie haben gelernt, dass const-Elementfunktionen keine Elementvariablen ändern können. Aber manchmal müssen Sie ein Element ändern, das den logischen Zustand des Objekts nicht beeinflusst – wie etwa einen Cache oder einen Zugriffszähler. Das Schlüsselwort mutable bietet eine Ausnahme von der const-Einschränkung.
Ein mutable Member kann sogar innerhalb einer const-Member-Funktion modifiziert werden:
class DataFetcher {
std::string data;
mutable int accessCount; // Kann in const-Funktionen modifiziert werden
public:
DataFetcher(std::string d) : data(d), accessCount(0) {}
std::string getData() const {
accessCount++; // Erlaubt, da accessCount mutable ist
return data;
}
int getAccessCount() const {
return accessCount;
}
};Ohne mutable würde das Inkrementieren von accessCount innerhalb der const-Funktion getData() einen Compiler-Fehler verursachen. Das mutable-Schlüsselwort teilt dem Compiler mit: „Dieses Element darf sich ändern, selbst wenn das Objekt als const betrachtet wird.“
Häufige Anwendungsfälle für mutable umfassen das Caching berechneter Werte, das Verfolgen von Zugriffsstatistiken und Synchronisationsprimitive wie Mutexe. In jedem Fall ändert die Modifikation nicht das, was das Objekt logisch repräsentiert – ein gecachter Wert ist lediglich eine Optimierung, und ein Zugriffsrechner sind Metadaten über die Nutzung.
class Circle {
double radius;
mutable double cachedArea;
mutable bool areaCached;
public:
double getArea() const {
if (!areaCached) {
cachedArea = 3.14159 * radius * radius;
areaCached = true;
}
return cachedArea;
}
};Verwenden Sie mutable sparsam – es sollte nur auf Member angewendet werden, die den beobachtbaren Zustand des Objekts nicht beeinflussen.
Aufgabe
EinfachLassen Sie uns einen Dokumentenbetrachter erstellen, der verfolgt, wie oft ein Dokument angesehen wurde — ohne die Const-Korrektheit unserer schreibgeschützten Methoden zu verletzen. Dies ist ein perfekter Anwendungsfall für das Schlüsselwort mutable.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
Document.h: Definieren Sie eine KlasseDocument, die Inhalte speichert und heimlich Zugriffsstatistiken verfolgt. Ihre Klasse benötigt:- Private Member:
title(string),content(string) und einmutable int viewCount, das selbst in const-Methoden geändert werden kann - Einen Konstruktor, der einen Titel und Inhalt entgegennimmt und
viewCountmit0initialisiert - Eine const-Methode
getTitle(), die den Titel zurückgibt - Eine const-Methode
getContent(), dieviewCounterhöht (erlaubt, da es mutable ist) und den Inhalt zurückgibt - Eine const-Methode
getViewCount(), die zurückgibt, wie oft auf den Inhalt zugegriffen wurde - Eine const-Methode
getPreview(), die die ersten 20 Zeichen des Inhalts gefolgt von"..."zurückgibt — dies sollte den viewCount NICHT erhöhen, da es nur eine Vorschau ist
- Private Member:
main.cpp: Demonstrieren Sie, wie mutable das Verfolgen von Zugriffsstatistiken ermöglicht, während die Methoden logisch const bleiben. Lesen Sie einen Dokumenttitel und Inhalt von der Eingabe (jeweils in einer separaten Zeile) und führen Sie dann Folgendes aus:- Erstellen Sie ein
Documentmit den Eingabewerten - Geben Sie
"Title: <title>"aus - Geben Sie
"Preview: <preview>"aus - Geben Sie
"Views after preview: <count>"aus - Greifen Sie mit
getContent()auf den vollständigen Inhalt zu und geben Sie"Content: <content>"aus - Geben Sie
"Views after first read: <count>"aus - Greifen Sie erneut auf den Inhalt zu und geben Sie
"Content: <content>"aus - Geben Sie
"Views after second read: <count>"aus - Erstellen Sie eine Hilfsfunktion
void displayDocument(const Document& doc), die"[Const Access] <title>: <content>"ausgibt — dies beweist, dass unsere const-Methoden mit const-Referenzen funktionieren - Rufen Sie
displayDocument()mit Ihrem Dokument auf - Geben Sie
"Final view count: <count>"aus
- Erstellen Sie ein
Der viewCount sollte nach der Vorschau 0 sein (da Vorschauen nicht als vollständige Ansichten zählen) und sich dann jedes Mal erhöhen, wenn getContent() aufgerufen wird. Das Schlüsselwort mutable macht dies möglich — ohne es könnten Sie viewCount innerhalb von const-Methoden wie getContent() nicht ändern.
Wenn der Inhalt kürzer als 20 Zeichen ist, sollte getPreview() den gesamten Inhalt gefolgt von "..." zurückgeben.
Spickzettel
Das Schlüsselwort mutable ermöglicht es, eine Elementvariable selbst innerhalb von const-Elementfunktionen zu ändern:
class DataFetcher {
std::string data;
mutable int accessCount; // Kann in const-Funktionen geändert werden
public:
DataFetcher(std::string d) : data(d), accessCount(0) {}
std::string getData() const {
accessCount++; // Erlaubt, da accessCount mutable ist
return data;
}
int getAccessCount() const {
return accessCount;
}
};Ohne mutable würde das Ändern einer Elementvariablen innerhalb einer const-Funktion zu einem Compiler-Fehler führen. Das Schlüsselwort teilt dem Compiler mit, dass dieses Element geändert werden darf, selbst wenn das Objekt als const betrachtet wird.
Häufige Anwendungsfälle sind das Caching berechneter Werte, das Verfolgen von Zugriffsstatistiken und Synchronisationsprimitive. Die Änderung sollte nicht das verändern, was das Objekt logisch repräsentiert:
class Circle {
double radius;
mutable double cachedArea;
mutable bool areaCached;
public:
double getArea() const {
if (!areaCached) {
cachedArea = 3.14159 * radius * radius;
areaCached = true;
}
return cachedArea;
}
};Verwenden Sie mutable sparsam – nur für Elemente, die den beobachtbaren Zustand des Objekts nicht beeinflussen.
Probier es selbst
#include <iostream>
#include <string>
#include "Document.h"
using namespace std;
// TODO: Implementiere die displayDocument Funktion
// Sie sollte Folgendes ausgeben: "[Const Access] <title>: <content>"
void displayDocument(const Document& doc) {
// TODO: Implementiere diese Funktion
}
int main() {
// Eingabe lesen
string title;
string content;
getline(cin, title);
getline(cin, content);
// TODO: Erstelle ein Document mit den Eingabewerten
// TODO: Gib "Title: <title>" aus
// TODO: Gib "Preview: <preview>" aus
// TODO: Gib "Views after preview: <count>" aus
// TODO: Greife auf den vollständigen Inhalt zu und gib "Content: <content>" aus
// TODO: Gib "Views after first read: <count>" aus
// TODO: Greife erneut auf den Inhalt zu und gib "Content: <content>" aus
// TODO: Gib "Views after second read: <count>" aus
// TODO: Rufe displayDocument() mit deinem Dokument auf
// TODO: Gib "Final view count: <count>" aus
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container