Const-Memberfunktionen
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 29 von 104.
Sie haben das Schlüsselwort const bei Getter-Methoden in der vorherigen Lektion gesehen. Aber was genau bedeutet es, wenn const nach der Parameterliste einer Funktion erscheint? Eine const-Elementfunktion verspricht, keine Elementvariablen des Objekts zu verändern.
class Rectangle {
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {}
int getArea() const { // Konstante Elementfunktion
return width * height; // Lesen von Membern ist erlaubt
}
void setWidth(int w) { // Nicht-const - modifiziert das Objekt
width = w;
}
};Das const nach der Parameterliste teilt dem Compiler mit: „Diese Funktion wird den Zustand des Objekts nicht ändern.“ Wenn Sie versuchen, eine Elementvariable innerhalb einer const-Funktion zu ändern, wird der Compiler einen Fehler erzeugen.
Dies wird unerlässlich, wenn man mit const-Objekten oder const-Referenzen arbeitet. Ein const-Objekt kann nur const-Memberfunktionen aufrufen:
void printArea(const Rectangle& rect) {
std::cout << rect.getArea(); // OK - getArea() ist const
// rect.setWidth(10); // FEHLER - setWidth() ist nicht const
}Es ist eine gute Praxis, Funktionen als const zu kennzeichnen, wenn sie das Objekt nicht verändern. Es dokumentiert Ihre Absicht, ermöglicht es der Funktion, mit const-Objekten zu arbeiten, und hilft dem Compiler, versehentliche Änderungen zu erkennen. Jede Elementfunktion, die nur Daten liest, sollte als const markiert werden.
Aufgabe
EinfachLassen Sie uns einen Temperaturkonverter bauen, der demonstriert, wann und warum man const-Memberfunktionen verwendet. Sie werden eine Klasse erstellen, in der einige Methoden nur Daten lesen (und const sein sollten), während andere den Zustand des Objekts ändern.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
Temperature.h: Definieren Sie eineTemperature-Klasse, die einen Temperaturwert speichert und verschiedene Möglichkeiten bietet, diesen zu lesen und zu ändern. Ihre Klasse sollte Folgendes haben:- Ein privates Member
celsius(double), um die Temperatur zu speichern - Einen Konstruktor, der einen initialen Celsius-Wert entgegennimmt
- Eine
getCelsius()-Methode, die den gespeicherten Wert zurückgibt — diese sollte const sein, da sie nur Daten liest - Eine
getFahrenheit()-Methode, welche die Temperatur in Fahrenheit mit der Formelcelsius * 9.0 / 5.0 + 32.0berechnet und zurückgibt — ebenfalls const, da sie nichts modifiziert - Eine
getKelvin()-Methode, welche die Temperatur in Kelvin mitcelsius + 273.15zurückgibt — ebenfalls const - Eine
setCelsius(double value)-Methode, die die gespeicherte Temperatur aktualisiert — diese kann nicht const sein, da sie das Objekt modifiziert - Eine
adjustBy(double delta)-Methode, die das Delta zur aktuellen Temperatur addiert — ebenfalls nicht-const
- Ein privates Member
main.cpp: Demonstrieren Sie, wie const-Memberfunktionen mit sowohl regulären als auch const-Objekten funktionieren. Lesen Sie einen initialen Temperaturwert von der Eingabe ein und führen Sie dann Folgendes aus:- Erstellen Sie ein
Temperature-Objekt mit dem Eingabewert - Geben Sie
"Initial: <celsius>C = <fahrenheit>F = <kelvin>K"aus - Passen Sie die Temperatur um
10.0Grad an - Geben Sie
"After adjustment: <celsius>C"aus - Erstellen Sie eine Hilfsfunktion
void printReadings(const Temperature& temp), die eine const-Referenz entgegennimmt und"Reading: <celsius>C, <fahrenheit>F"ausgibt — diese Funktion kann nur const-Methoden auf temp aufrufen - Rufen Sie
printReadings()mit Ihrem Temperatur-Objekt auf - Setzen Sie die Temperatur auf
0.0(Gefrierpunkt) - Geben Sie
"Freezing point: <celsius>C = <fahrenheit>F"aus
- Erstellen Sie ein
Formatieren Sie alle Temperaturwerte mit einer Dezimalstelle unter Verwendung von std::fixed und std::setprecision(1) aus <iomanip>.
Die entscheidende Erkenntnis hierbei ist, dass printReadings() eine const-Referenz erhält und daher nur Methoden aufrufen kann, die als const markiert sind. Aus diesem Grund ist es wichtig, Ihre Getter-Methoden korrekt als const zu markieren — es ermöglicht deren Verwendung in Kontexten, in denen das Objekt nicht modifiziert werden kann.
Spickzettel
Eine const-Elementfunktion verspricht, keine Elementvariablen des Objekts zu verändern. Das Schlüsselwort const wird hinter der Parameterliste der Funktion platziert:
class Rectangle {
int width;
int height;
public:
int getArea() const { // const-Elementfunktion
return width * height; // Lesen von Elementen ist erlaubt
}
void setWidth(int w) { // Nicht-const - verändert das Objekt
width = w;
}
};Const-Elementfunktionen können Elementvariablen nur lesen, nicht verändern. Wenn Sie versuchen, eine Elementvariable innerhalb einer const-Funktion zu verändern, wird der Compiler einen Fehler erzeugen.
Ein const-Objekt oder eine const-Referenz kann nur const-Elementfunktionen aufrufen:
void printArea(const Rectangle& rect) {
std::cout << rect.getArea(); // OK - getArea() ist const
// rect.setWidth(10); // FEHLER - setWidth() ist nicht const
}Markieren Sie Funktionen als const, wenn sie das Objekt nicht verändern. Dies dokumentiert Ihre Absicht, ermöglicht der Funktion die Arbeit mit const-Objekten und hilft dem Compiler, versehentliche Änderungen zu erkennen. Jede Elementfunktion, die nur Daten liest, sollte als const markiert werden.
Probier es selbst
#include <iostream>
#include <iomanip>
#include "Temperature.h"
using namespace std;
// TODO: Implementiere hier die Methoden der Klasse Temperature
// Konstruktor
Temperature::Temperature(double initialCelsius) {
// TODO: Initialisiere celsius
}
// TODO: Implementiere getCelsius() als const
// TODO: Implementiere getFahrenheit() als const
// TODO: Implementiere getKelvin() als const
// TODO: Implementiere setCelsius(double value)
// TODO: Implementiere adjustBy(double delta)
// TODO: Erstelle eine Hilfsfunktion printReadings, die eine const Temperature& entgegennimmt
// und "Reading: <celsius>C, <fahrenheit>F" ausgibt
// Note: Diese Funktion kann nur const-Methoden auf temp aufrufen!
int main() {
double initialTemp;
cin >> initialTemp;
// Ausgabeformatierung festlegen
cout << fixed << setprecision(1);
// TODO: Erstelle ein Temperature-Objekt mit dem Eingabewert
// TODO: Gib "Initial: <celsius>C = <fahrenheit>F = <kelvin>K" aus
// TODO: Passe die Temperatur um 10.0 Grad an
// TODO: Gib "After adjustment: <celsius>C" aus
// TODO: Rufe printReadings() mit deinem Temperature-Objekt auf
// TODO: Setze die Temperatur auf 0.0 (Gefrierpunkt)
// TODO: Gib "Freezing point: <celsius>C = <fahrenheit>F" 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