Instanz- vs. statische Member
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 27 von 104.
Bisher gehörte jede Elementvariable, die Sie erstellt haben, zu einem bestimmten Objekt. Wenn Sie zwei Player-Objekte erstellen, hat jedes seinen eigenen name und seine eigene health. Diese werden als Instanzelemente bezeichnet – sie existieren separat für jede Instanz der Klasse.
Aber was ist, wenn Sie Daten benötigen, die über alle Objekte hinweg geteilt werden? Zum Beispiel, um zu verfolgen, wie viele Spieler insgesamt existieren.
Hier kommen statische Member ins Spiel. Ein statisches Member gehört zur Klasse selbst, nicht zu einem bestimmten Objekt.
class Player {
std::string name; // Instanz-Member - jeder Spieler hat seinen eigenen
int health; // Instanz-Member
static int playerCount; // Statisches Member - wird von ALLEN Spielern geteilt
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // Erhöhen, wenn ein Spieler erstellt wird
}
~Player() {
playerCount--; // Verringern, wenn ein Spieler zerstört wird
}
};Der entscheidende Unterschied: Auf Instanz-Member wird über ein Objekt zugegriffen, während auf statische Member über den Klassennamen selbst zugegriffen werden kann. Betrachten Sie Instanz-Member als „eines pro Objekt“ und statische Member als „eines pro Klasse“.
| Instanz-Member | Statische Member |
|---|---|
| Eindeutig für jedes Objekt | Wird von allen Objekten geteilt |
| Wird erstellt, wenn das Objekt erstellt wird | Existiert, bevor ein Objekt erstellt wird |
Zugriff über das Objekt: player.health | Zugriff über die Klasse: Player::playerCount |
Statische Member sind nützlich für Zähler, Konfigurationswerte oder alle Daten, die logisch eher zur Klasse als zu einzelnen Objekten gehören.
Aufgabe
EinfachLassen Sie uns ein Produktinventarsystem erstellen, das sowohl einzelne Produktdetails als auch allgemeine Inventarstatistiken mithilfe von Instanz- und statischen Membern verfolgt.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
Product.h: Definieren Sie eine KlasseProduct, die einzelne Produkte verwaltet und gleichzeitig inventarweite Daten verfolgt. Ihre Klasse sollte Folgendes enthalten:- Instanz-Member:
name(string) undprice(double) — eindeutig für jedes Produkt - Ein statisches Member
totalProducts, das zählt, wie viele Produkte derzeit existieren - Ein statisches Member
totalValue, das den kombinierten Preis aller Produkte verfolgt - Einen Konstruktor, der einen Namen und einen Preis entgegennimmt, die Instanz-Member initialisiert und beide statischen Zähler aktualisiert (Anzahl erhöhen, Preis zum Gesamtwert addieren)
- Einen Destruktor, der die statischen Zähler aktualisiert, wenn ein Produkt entfernt wird (Anzahl verringern, Preis vom Gesamtwert abziehen)
- Eine Methode
getInfo(), die einen String im Format"<name>: "<name>: $<price>"lt;price>"zurückgibt - Eine statische Methode
getInventoryStats(), die einen String zurückgibt:"Products: <count>, Total Value: "Products: <count>, Total Value: $<value>"lt;value>"
- Instanz-Member:
main.cpp: Demonstrieren Sie, wie statische Member von allen Objekten gemeinsam genutzt werden, während Instanz-Member eindeutig bleiben. Lesen Sie zwei Produktnamen und deren Preise von der Eingabe ein (name1, price1, name2, price2 — jeweils in einer separaten Zeile), dann:- Geben Sie die anfänglichen Inventarstatistiken unter Verwendung des Klassennamens aus:
Product::getInventoryStats() - Erstellen Sie einen Scope-Block mit geschweiften Klammern und erstellen Sie darin ein
Productmit dem ersten Namen und Preis. Geben Sie dessen Info und die aktuellen Inventarstatistiken aus. - Nachdem der Block endet (das erste Produkt wird zerstört), geben Sie
"After first scope:"gefolgt von den Inventarstatistiken aus - Erstellen Sie ein weiteres
Productmit dem zweiten Namen und Preis. Geben Sie dessen Info und die Inventarstatistiken aus. - Erstellen Sie ein drittes
Productmit dem Namen"Bonus"und dem Preis5.00. Geben Sie die finalen Inventarstatistiken aus.
- Geben Sie die anfänglichen Inventarstatistiken unter Verwendung des Klassennamens aus:
Denken Sie daran, Ihre statischen Member außerhalb der Klasse in der Header-Datei (nach der Klassendefinition) zu definieren. Formatieren Sie Preise mit zwei Dezimalstellen unter Verwendung von std::fixed und std::setprecision(2) aus <iomanip>.
Binden Sie Ihre Header-Datei in main.cpp mit #include "Product.h" ein.
Spickzettel
C++ unterstützt zwei Arten von Klassenmitgliedern: Instanzmitglieder und statische Mitglieder.
Instanzmitglieder sind für jedes Objekt eindeutig – jede Instanz hat ihre eigene Kopie:
class Player {
std::string name; // Jeder Spieler hat seinen eigenen Namen
int health; // Jeder Spieler hat seine eigene Gesundheit
};Statische Mitglieder werden von allen Objekten einer Klasse gemeinsam genutzt – es gibt nur eine Kopie, die der Klasse selbst gehört:
class Player {
static int playerCount; // Wird von ALLEN Spielern geteilt
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // Gemeinsamen Zähler erhöhen
}
~Player() {
playerCount--; // Verringern, wenn zerstört
}
};Wesentliche Unterschiede:
| Instanzmitglieder | Statische Mitglieder |
|---|---|
| Eindeutig für jedes Objekt | Wird von allen Objekten geteilt |
| Wird erstellt, wenn das Objekt erstellt wird | Existiert, bevor ein Objekt erstellt wird |
Zugriff über Objekt: player.health | Zugriff über Klasse: Player::playerCount |
Statische Methoden können über den Klassennamen aufgerufen werden und können nur auf statische Mitglieder zugreifen:
class Player {
static int playerCount;
public:
static int getPlayerCount() {
return playerCount; // Kann nur auf statische Mitglieder zugreifen
}
};
// Aufruf über Klassennamen
int count = Player::getPlayerCount();Statische Mitglieder müssen außerhalb der Klasse definiert werden (normalerweise in einer .cpp-Datei oder nach der Klassendefinition in einem Header):
int Player::playerCount = 0;Statische Mitglieder sind nützlich für Zähler, gemeinsam genutzte Konfigurationswerte oder alle Daten, die logischerweise zur Klasse und nicht zu einzelnen Objekten gehören.
Probier es selbst
#include <iostream>
#include <string>
#include "Product.h"
using namespace std;
int main() {
// Eingabe lesen
string name1;
double price1;
string name2;
double price2;
getline(cin, name1);
cin >> price1;
cin.ignore();
getline(cin, name2);
cin >> price2;
// TODO: Anfängliche Inventarstatistiken mit Product::getInventoryStats() ausgeben
// TODO: Einen Scope-Block mit geschweiften Klammern erstellen
// Innerhalb des Blocks:
// - Ein Product mit name1 und price1 erstellen
// - Dessen Informationen mit getInfo() ausgeben
// - Aktuelle Inventarstatistiken ausgeben
// TODO: Nach dem Ende des Blocks "After first scope:" ausgeben
// Dann die Inventarstatistiken ausgeben
// TODO: Ein weiteres Product mit name2 und price2 erstellen
// Dessen Informationen und die Inventarstatistiken ausgeben
// TODO: Ein drittes Product mit dem Namen "Bonus" und dem Preis 5.00 erstellen
// Die finalen Inventarstatistiken ausgeben
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