Mutable-Schlüsselwort
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 30 von 104.
Du hast gelernt, dass const-Memberfunktionen keine Membervariablen ändern können. Aber manchmal musst du ein Member ändern, das den logischen Zustand des Objekts nicht beeinflusst – etwa einen Cache oder einen Zugriffszähler. Das Schlüsselwort mutable bietet eine Ausnahme von der const-Beschränkung.
Ein veränderbares Mitglied kann sogar innerhalb einer const-Memberfunktion geändert werden:
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, weil accessCount mutable ist
return data;
}
int getAccessCount() const {
return accessCount;
}
};Ohne mutable würde das Erhöhen von accessCount innerhalb der const-Funktion getData() einen Compilerfehler verursachen. Das Schlüsselwort mutable teilt dem Compiler mit: „Dieses Member darf geändert werden, selbst wenn das Objekt als const betrachtet wird.“
Häufige Anwendungsfälle für veränderliche Werte sind das Zwischenspeichern berechneter Werte, das Verfolgen von Zugriffstatistiken und Synchronisierungsprimitive wie Mutexes. In jedem Fall ändert die Modifikation nicht, was das Objekt logisch darstellt – ein zwischengespeicherter Wert ist lediglich eine Optimierung, und ein Zugriffszähler 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;
}
};Verwende mutable sparsam – es sollte nur auf Member angewendet werden, die den beobachtbaren Zustand des Objekts nicht beeinflussen.
Aufgabe
EinfachLass uns einen Dokumentbetrachter erstellen, der verfolgt, wie oft ein Dokument angesehen wurde, ohne die Const-Korrektheit unserer schreibgeschützten Methoden zu beeinträchtigen. Dies ist ein perfekter Anwendungsfall für das Schlüsselwort mutable.
Du wirst zwei Dateien erstellen, um deinen Code zu organisieren:
Document.h: Definiere eineDocument-Klasse, die den Inhalt speichert und heimlich die Ansichtsstatistiken verfolgt. Deine Klasse benötigt:- Private Member:
title(String),content(String) und einmutable int viewCount, das auch in Const-Methoden geändert werden kann - Einen Konstruktor, der einen Titel und einen Inhalt entgegennimmt und
viewCountauf0initialisiert - Eine Const-Methode
getTitle(), die den Titel zurückgibt - Eine Const-Methode
getContent(), dieviewCounterhöht (weil 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. Diese Methode darf die Anzahl der Ansichten NICHT erhöhen, da es sich nur um eine Vorschau handelt
- Private Member:
main.cpp: Zeige, wie mutable das Verfolgen von Zugriffsstatistiken ermöglicht, während die Methoden logisch const bleiben. Lies einen Dokumenttitel und einen Inhalt aus der Eingabe ein (jeweils in einer separaten Zeile), und dann:- Erstelle ein
Documentmit den Eingabewerten - Gib
"Title: <title>"aus - Gib
"Preview: <preview>"aus - Gib
"Views after preview: <count>"aus - Greife mit
getContent()auf den vollständigen Inhalt zu und gib"Content: <content>"aus - Gib
"Views after first read: <count>"aus - Greife erneut auf den Inhalt zu und gib
"Content: <content>"aus - Gib
"Views after second read: <count>"aus - Erstelle eine Hilfsfunktion
void displayDocument(const Document& doc), die"[Const Access] <title>: <content>"ausgibt. Dies beweist, dass unsere Const-Methoden mit Const-Referenzen funktionieren - Rufe
displayDocument()mit deinem Dokument auf - Gib
"Final view count: <count>"aus
- Erstelle ein
Die Anzahl der Ansichten sollte nach der Vorschau 0 betragen (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önntest du viewCount nicht innerhalb von Const-Methoden wie getContent() ändern.
Wenn der Inhalt kürzer als 20 Zeichen ist, sollte getPreview() den gesamten Inhalt gefolgt von "..." zurückgeben.
Probier es selbst
#include <iostream>
#include <string>
#include "Document.h"
using namespace std;
// TODO: Implementiere die displayDocument-Funktion
// Sie sollte 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: Print "Views after preview: <count>"
// TODO: Greife auf den vollständigen Inhalt zu und gib "Content: <content>" aus
// TODO: Print "Views after first read: <count>"
// TODO: Greife erneut auf den Inhalt zu und gib "Content: <content>" aus
// TODO: Print "Views after second read: <count>"
// TODO: Rufe displayDocument() mit deinem Document auf
// TODO: Print "Final view count: <count>"
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 & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler