Geschichte der Frage:
C++ wurde auf der Grundlage von C entwickelt, das eine kleine Anzahl von eingebauten Typen bereitstellt: Zahlen, Zeichen, Arrays. Mit der Weiterentwicklung der Sprache kamen Konzepte wie Strukturen, Klassen, Enumerationen hinzu – sie wurden zu benutzerdefinierten Typen.
Problem:
Der Datentyp in einem Programm bestimmt, wie viel Speicher eine Variable benötigt, wie sie initialisiert, kopiert, zerstört und verglichen wird. Eingebaute Typen haben ein standardmäßig definiertes Verhalten, während benutzerdefinierte Typen eine explizite Beschreibung aller Aspekte erfordern. Fehler im Umgang mit benutzerdefinierten Typen können zu Abstürzen, Speicherlecks, unfairen Objektvergleichen usw. führen.
Lösung:
Eingebaute Typen sind int, float, double, char, bool und andere "Primitive". Benutzerdefinierte Typen sind beliebige Strukturen, Klassen, Vereinigungen, die Sie erstellt haben. Für komplexe Aufgaben ist es notwendig, benutzerdefinierte Typen mit korrekter Semantik für Kopieren, Vergleichen und Ressourcenverwaltung zu implementieren.
Beispielcode:
// Eingebauter Typ int x = 5; // Benutzerdefinierter Typ struct Point { double x, y; }; Point a = {1.0, 2.0}; // Klasse mit Ressourcen class MyFile { public: MyFile(const std::string& fn) : f(fopen(fn.c_str(), "r")) {} ~MyFile() { if (f) fclose(f); } private: FILE* f; };
Können sich benutzerdefinierte Typen vollständig wie eingebaute Typen verhalten (zum Beispiel über == "out of the box" verglichen werden)?
Nein, der Vergleich == funktioniert standardmäßig erst ab C++20 über defaulted operator==, davor war eine explizite Definition erforderlich.
Kann man den Kopierkonstruktor und den Destruktor weglassen, wenn die Klasse nur einen rohen Zeiger (int, FILE usw.) hält?**
Nein, in diesem Fall wird das Kopieren standardmäßig "flach" sein, was zu Speicherlecks oder doppelter Freigabe von Speicher/Ressourcen führen kann. Es muss die "Rule of Five" implementiert werden.
Wird der Wert der Struktur standardmäßig mit Nullen (Point p;?) initialisiert?
Nein, eine lokale Struktur könnte uninitialisiert sein, der Inhalt des Speichers ist zufällig. Verwenden Sie explizit die Initialisierung.
Negativer Fall:
Eine Datei-Wrapper-Klasse hat keinen Destruktor implementiert. Das Programm funktionierte, bis es Tausende von Dateien ohne Schließung der Deskriptoren verarbeitete.
Vorteile: weniger Code, vereinfachte Sicht Nachteile: Ressourcenlecks, instabiles Verhalten
Positiver Fall:
Die Klasse implementiert RAII – die Datei wird im Konstruktor geöffnet, im Destruktor geschlossen, das Kopieren ist verboten.
Vorteile: Zuverlässigkeit, Sicherheit, sauberes Interface Nachteile: Notwendigkeit, sich an die "Rule of Five" zu erinnern und mehr Code zu schreiben