sizeof und Typumwandlungint, float, double,
char, und bool. Initialisieren Sie diese mit
beliebigen Werten und geben Sie sie mithilfe von std::cout
aus.sizeof
int, float, double,
char, und bool mithilfe der Funktion
sizeof ausgibt.double Variable mit dem Wert
9.7. Wandeln Sie diese in eine int-Variable um
und geben Sie beide Werte aus.int um. Was wird ausgegeben und warum?a = 5 und
b = 2. Teilen Sie a durch b und
speichern Sie das Ergebnis in einer double-Variable. Was
müssen Sie tun, um ein korrektes Ergebnis zu erhalten?Grundlegende Datentypen
int i = 42;
float f = 3.14f;
double d = 2.71828;
char c = 'G';
bool b = true;
std::cout << i << " " << f << " " << d << " " << c << " " << b << std::endl;Verwendung von sizeof
std::cout << "int: " << sizeof(int) << " bytes\n";
std::cout << "float: " << sizeof(float) << " bytes\n";
std::cout << "double: " << sizeof(double) << " bytes\n";
std::cout << "char: " << sizeof(char) << " byte\n";
std::cout << "bool: " << sizeof(bool) << " byte(s)\n";Typumwandlung
double dValue = 9.7;
int iValue = static_cast<int>(dValue);
std::cout << "Double: " << dValue << "\nInt: " << iValue << std::endl;
char ch = 'A';
int chAsInt = static_cast<int>(ch);
std::cout << "Char: " << ch << "\nInt: " << chAsInt << std::endl; // Gibt den ASCII-Wert von 'A' aus, welcher 65 ist.Arithmetik und Typumwandlung
int a = 5, b = 2;
double result = static_cast<double>(a) / b;
std::cout << "Result: " << result << std::endl; // Das korrekte Ergebnis ist 2.5.auto
5.0 und lassen Sie
den Compiler mithilfe von auto den Typ bestimmen. Welchen
Typ wird die Variable haben?auto
std::vector<int> mit einigen
Werten. Verwenden Sie dann eine for-Schleife und
auto, um über den Vektor zu iterieren und die Werte
auszugeben.auto
auto als Rückgabetyp der
Funktion und testen Sie die Funktion mit verschiedenen Datentypen (z.B.
int, double).auto
std::map<std::string, std::vector<int>> und
fügen Sie einige Werte hinzu. Verwenden Sie dann auto, um
über die Map zu iterieren und die Schlüssel-Wert-Paare auszugeben.Verwendung von auto
auto var = 5.0;
std::cout << typeid(var).name() << std::endl; // Dies wird "double" sein.Iterieren mit auto
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (auto num : numbers) {
std::cout << num << " ";
}Funktionsrückgabetyp mit auto
auto multiply(auto a, auto b) {
return a * b;
}
std::cout << multiply(2, 5) << std::endl; // 10
std::cout << multiply(2.5, 3.0) << std::endl; // 7.5Komplexe Typen und auto
std::map<std::string, std::vector<int>> data = {
{"Alice", {1, 2, 3}},
{"Bob", {4, 5, 6}}
};
for (auto const& pair : data) {
std::cout << pair.first << ": ";
for (auto num : pair.second) {
std::cout << num << " ";
}
std::cout << std::endl;
}alter und
initialisieren Sie sie mit Ihrem Alter.durchschnittsnote
und initialisieren Sie sie mit einer Zahl zwischen 1.0 und 5.0.std::cout
aus.zahlen
mit den Werten 5, 10, 15, 20.noten mit fünf Noten Ihrer Wahl.Variablen-Grundlagen
int alter = 25;
float durchschnittsnote = 3.2f;
std::cout << "Alter: " << alter << "\nDurchschnittsnote: " << durchschnittsnote << std::endl;Einfache Array-Operationen
int zahlen[] = {5, 10, 15, 20};
zahlen[2] = 25;
for(int i = 0; i < 4; i++) {
std::cout << zahlen[i] << " ";
}
std::cout << std::endl;Berechnung des Durchschnitts
double noten[] = {1.5, 2.0, 2.5, 3.0, 4.0};
double summe = 0.0;
for(int i = 0; i < 5; i++) {
summe += noten[i];
}
double durchschnitt = summe / 5;
std::cout << "Durchschnitt: " << durchschnitt << std::endl;name und initialisieren Sie
ihn mit Ihrem Namen.begrüßung und verwenden Sie
den name, um einen Begrüßungstext zu erzeugen, wie z.B.
“Hallo, [Ihr Name]!”.begrüßung mithilfe von std::cout
aus.wort mit dem Wert
“Programmieren”.satz mit dem Wert “C++ ist
eine wunderbare Programmiersprache.”substr() und geben Sie es aus.string1 mit dem Wert
“Hello” und string2 mit dem Wert “World”.umkehrung mit dem Wert
“abcd”.Grundlagen der String-Manipulation
std::string name = "Anna";
std::string begrüßung = "Hallo, " + name + "!";
std::cout << begrüßung << std::endl;String-Länge
std::string wort = "Programmieren";
std::cout << wort.size() << std::endl;Substring und Suche
std::string satz = "C++ ist eine wunderbare Programmiersprache.";
size_t position = satz.find("wunderbare");
if (position != std::string::npos) {
std::cout << "Position: " << position << std::endl;
std::cout << "Substring: " << satz.substr(position, 10) << std::endl;
}String-Konkatenation
std::string string1 = "Hello";
std::string string2 = "World";
std::cout << string1 + " " + string2 << std::endl;Umkehrung eines Strings
std::string umkehrung = "abcd";
std::reverse(umkehrung.begin(), umkehrung.end());
std::cout << umkehrung << std::endl;gruß(), die “Hallo, Welt!”
ausgibt.main() auf.quadrieren(int num), die
eine Zahl als Parameter akzeptiert und ihr Quadrat zurückgibt.main() auf und geben Sie
das Ergebnis aus.tausche(int &a, int &b), die zwei Zahlen
vertauscht.main() auf und überprüfen
Sie, ob die Werte vertauscht wurden.druckeDaten().
Eine sollte einen int akzeptieren und die andere einen
std::string.main()
auf.faktorial(int n), die den Faktorial einer Zahl
zurückgibt.main() auf und geben Sie
das Ergebnis aus.Einfache Funktion
void gruß() {
std::cout << "Hallo, Welt!" << std::endl;
}
int main() {
gruß();
return 0;
}Funktion mit Parameter (Call by Value)
int quadrieren(int num) {
return num * num;
}
int main() {
int zahl = 5;
std::cout << quadrieren(zahl) << std::endl; // Sollte 25 ausgeben
return 0;
}Funktion mit Referenzparameter (Call by Reference)
void tausche(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 5, y = 10;
tausche(x, y);
std::cout << "x: " << x << ", y: " << y << std::endl; // Sollte "x: 10, y: 5" ausgeben
return 0;
}Überladene Funktionen
void druckeDaten(int num) {
std::cout << "Integer: " << num << std::endl;
}
void druckeDaten(std::string text) {
std::cout << "Text: " << text << std::endl;
}
int main() {
druckeDaten(5); // Sollte "Integer: 5" ausgeben
druckeDaten("C++"); // Sollte "Text: C++" ausgeben
return 0;
}Rekursion
int faktorial(int n) {
if (n <= 1) {
return 1;
}
return n * faktorial(n - 1);
}
int main() {
std::cout << faktorial(5) << std::endl; // Sollte 120 ausgeben
return 0;
}a, b, summe, differenz.a und b beliebige Werte
zu.a und
b und speichern Sie die Ergebnisse in den entsprechenden
Variablen.x
gerade oder ungerade ist und geben Sie das Ergebnis aus.double Werten und berechnen
Sie den Durchschnitt der Zahlen in diesem Array.P, Zinssatz r und Zeitraum t mit
der Formel ( Zins = P r t ).pow() und sqrt().Grundlegende Rechenoperationen
int a = 5, b = 3, summe, differenz;
summe = a + b;
differenz = a - b;
std::cout << "Summe: " << summe << ", Differenz: " << differenz << std::endl;Modulo-Operator
int x = 6;
if (x % 2 == 0) {
std::cout << x << " ist gerade." << std::endl;
} else {
std::cout << x << " ist ungerade." << std::endl;
}Berechnung des Durchschnitts
double zahlen[] = {10.5, 20.5, 30.5};
double summe = 0.0;
for (int i = 0; i < 3; i++) {
summe += zahlen[i];
}
double durchschnitt = summe / 3;
std::cout << "Durchschnitt: " << durchschnitt << std::endl;Zinsberechnung
double P = 1000.0, r = 0.05, t = 2.0;
double zins = P * r * t;
std::cout << "Zins: " << zins << "€" << std::endl;Quadrat und Wurzel
#include <cmath>
double num = 5.0;
double quad = pow(num, 2);
double wurzel = sqrt(quad);
std::cout << "Quadrat: " << quad << ", Wurzel: " << wurzel << std::endl;a = true und
b = false. Überprüfen Sie die Werte von
a AND b, a OR b und NOT a.x = 5 und y = 3 berechnet.z = 16. Führen Sie eine bitweise
Rechtsverschiebung um 2 Positionen durch.m = 12 und n = 25.
Berechnen Sie den bitweisen ODER- und XOR-Wert.p = 8.Logische Operatoren
bool a = true, b = false;
std::cout << (a && b) << std::endl; // Ausgabe: 0
std::cout << (a || b) << std::endl; // Ausgabe: 1
std::cout << (!a) << std::endl; // Ausgabe: 0Bitweise UND-Operation
int x = 5, y = 3;
std::cout << (x & y) << std::endl; // Ausgabe: 1Bitweise Rechtsverschiebung
int z = 16;
std::cout << (z >> 2) << std::endl; // Ausgabe: 4Bitweise ODER- und XOR-Operationen
int m = 12, n = 25;
std::cout << (m | n) << std::endl; // Ausgabe: 29
std::cout << (m ^ n) << std::endl; // Ausgabe: 21Eins-Komplement
int p = 8;
std::cout << (~p) << std::endl; // Ausgabe: -9If-Else-Struktur
int num = 5;
if (num > 0) {
std::cout << "Positiv" << std::endl;
} else if (num < 0) {
std::cout << "Negativ" << std::endl;
} else {
std::cout << "Null" << std::endl;
}Switch-Case-Struktur
int auswahl;
std::cin >> auswahl;
switch (auswahl) {
case 1:
std::cout << "Option 1 gewählt!" << std::endl;
break;
case 2:
std::cout << "Option 2 gewählt!" << std::endl;
break;
case 3:
std::cout << "Option 3 gewählt!" << std::endl;
break;
default:
std::cout << "Ungültige Option!" << std::endl;
break;
}While-Schleife
int i = 1;
while (i <= 10) {
std::cout << i << std::endl;
i++;
}For-Schleife
int n = 5;
int fakultät = 1;
for (int j = 1; j <= n; j++) {
fakultät *= j;
}
std::cout << "Fakultät von " << n << " ist " << fakultät << std::endl;Ternärer Operator
int a = 4, b = 7;
int größer = (a > b) ? a : b;
std::cout << "Größerer Wert: " << größer << std::endl;Do-While-Schleife
char eingabe;
do {
std::cout << "Geben Sie 'y' oder 'n' ein: ";
std::cin >> eingabe;
} while (eingabe != 'y' && eingabe != 'n');a mit dem Wert 10 und
weisen Sie ihr anschließend den Wert 20 zu.b, c
und d und weisen Sie ihnen in einer einzigen Zeile den Wert
5 zu.e von fünf Ganzzahlen
mithilfe einer Listenzuweisung.auto Schlüsselwort, um eine Variable
f zu initialisieren, die den Wert 3.14 hält.Person mit den Mitgliedern
name und alter. Erstellen Sie ein Objekt
dieser Struktur mithilfe der Listenzuweisung.Einfache Initialisierung und Zuweisung
int a = 10;
a = 20;Mehrfache Zuweisung
int b, c, d;
b = c = d = 5;Listenzuweisung
int e[5] = {1, 2, 3, 4, 5};Initialisierung mit auto
auto f = 3.14;Listenzuweisung für Strukturen
struct Person {
std::string name;
int alter;
};
Person p = {"Max", 25};Math, der eine
Funktion quadrat enthält, die das Quadrat einer Zahl
zurückgibt. Verwenden Sie die Funktion innerhalb des
Hauptprogramms.Geometrie, der wiederum
einen Namespace Zweidimensional enthält. In
Zweidimensional sollte eine Funktion
flaecheRechteck existieren, die die Fläche eines Rechtecks
berechnet.Daten, die entweder eine
Ganzzahl integerWert oder ein float
floatWert speichern kann. Initialisieren Sie die Union in
Ihrem Hauptprogramm mit beiden Typen (nicht gleichzeitig) und geben Sie
die Werte aus.using-Direktive in Ihr Hauptprogramm ein,
um auf die Funktion quadrat zuzugreifen.Einfacher Namespace
namespace Math {
int quadrat(int x) {
return x * x;
}
}
int main() {
std::cout << Math::quadrat(5) << std::endl; // 25
return 0;
}Verschachtelte Namespaces
namespace Geometrie {
namespace Zweidimensional {
float flaecheRechteck(float breite, float hoehe) {
return breite * hoehe;
}
}
}
int main() {
std::cout << Geometrie::Zweidimensional::flaecheRechteck(5.0f, 4.0f) << std::endl; // 20
return 0;
}Union
union Daten {
int integerWert;
float floatWert;
};
int main() {
Daten d;
d.integerWert = 42;
std::cout << d.integerWert << std::endl; // 42
d.floatWert = 3.14;
std::cout << d.floatWert << std::endl; // 3.14
return 0;
}Namespaces mit using-Direktive
namespace Math {
int quadrat(int x) {
return x * x;
}
}
using namespace Math;
int main() {
std::cout << quadrat(6) << std::endl; // 36
return 0;
}std::unique_ptr für ein dynamisch
zugewiesenes Integer-Array. Füllen Sie das Array und geben Sie seine
Inhalte aus.std::shared_ptr-Instanzen, die sich
denselben Speicherplatz teilen. Verändern Sie über einen der Pointer den
Wert und geben Sie ihn über den anderen Pointer aus.std::shared_ptr und
std::weak_ptr und demonstrieren Sie, wie
std::weak_ptr den zyklischen Bezug verhindert.Zeiger und dynamische Speicherzuweisung
int* ptr = new int;
*ptr = 42;
std::cout << *ptr << std::endl; // 42
delete ptr;Referenzen
void vertausche(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 5, y = 10;
vertausche(x, y);
std::cout << x << " " << y << std::endl; // 10 5
}std::unique_ptr
std::unique_ptr<int[]> arr(new int[5]{1, 2, 3, 4, 5});
for(int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}std::shared_ptr
std::shared_ptr<int> sp1 = std::make_shared<int>(10);
std::shared_ptr<int> sp2 = sp1;
*sp2 = 20;
std::cout << *sp1 << std::endl; // 20std::weak_ptr
std::shared_ptr<int> sp3 = std::make_shared<int>(30);
std::weak_ptr<int> wp = sp3;
// Nachdem der shared_ptr gelöscht wurde, überprüfen, ob der weak_ptr noch zugreifen kann
sp3.reset();
if (auto spt = wp.lock()) {
std::cout << *spt << std::endl;
} else {
std::cout << "sp3 ist nicht mehr verfügbar." << std::endl;
}std::string-Objekte. Verwenden Sie
die Move-Semantik, um den Wert von einem String zum anderen zu
übertragen, ohne eine Kopie zu erstellen.DynamischesArray, die ein
dynamisch zugewiesenes int-Array speichert. Implementieren Sie einen
Move-Konstruktor und einen Move-Zuweisungsoperator für diese
Klasse.weiterleiten, die ein
Argument akzeptiert und dieses Argument an eine andere Funktion
ziel weiterleitet. Verwenden Sie Template und
Rvalue-Referenzen, um das Perfect Forwarding zu implementieren.Move-Semantik mit std::string
std::string str1 = "Hallo Welt!";
std::string str2 = std::move(str1);
std::cout << "str2: " << str2 << std::endl; // "Hallo Welt!"Eigene Klasse mit Move-Konstruktor
class DynamischesArray {
int* data;
size_t size;
public:
DynamischesArray(size_t s) : size(s), data(new int[s]) {}
// Move-Konstruktor
DynamischesArray(DynamischesArray&& other) noexcept : data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
}
// Move-Zuweisungsoperator
DynamischesArray& operator=(DynamischesArray&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
~DynamischesArray() { delete[] data; }
};Perfect Forwarding mit Funktionen
template<typename T>
void ziel(T&& arg) {
// Hier können Sie mit arg arbeiten
std::cout << arg << std::endl;
}
template<typename T>
void weiterleiten(T&& arg) {
ziel(std::forward<T>(arg));
}
int main() {
std::string test = "Test String";
weiterleiten(test); // Lvalue
weiterleiten("Temp String"); // Rvalue
}enum namens Wochentag
für jeden Tag der Woche. Erstellen Sie eine Funktion, die einen Tag als
Parameter akzeptiert und den Namen des Tages ausgibt.enum namens Monat, bei
dem jeder Monat mit seiner durchschnittlichen Tageanzahl assoziiert ist.
Zum Beispiel sollte Januar mit 31 assoziiert werden.enum class
enum class namens
Ampel, die die Zustände Rot, Gelb
und Grün enthält. Erstellen Sie eine Funktion, die den
aktuellen Zustand einer Ampel als Parameter akzeptiert und einen
entsprechenden Hinweis ausgibt.enum und
enum class
enum-Typen mit
gleichnamigen Enumeratoren haben? Was ist mit
enum class?Grundlegende Enums
enum Wochentag {Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag};
void printTag(Wochentag tag) {
switch(tag) {
case Montag: std::cout << "Montag"; break;
// ... Andere Tage entsprechend ...
case Sonntag: std::cout << "Sonntag"; break;
}
}Enumeratoren mit spezifischen Werten
enum Monat {Januar = 31, Februar = 28 /* ... und so weiter ... */};Verwendung von enum class
enum class Ampel {Rot, Gelb, Grün};
void ampelStatus(Ampel a) {
switch(a) {
case Ampel::Rot: std::cout << "Stoppen"; break;
case Ampel::Gelb: std::cout << "Vorsicht"; break;
case Ampel::Grün: std::cout << "Weiterfahren"; break;
}
}Vergleich zwischen enum und
enum class
enum Farben1 {Rot, Blau, Grün};
enum Farben2 {Gelb, Blau, Schwarz}; // Kompilierungsfehler wegen "Blau"
enum class FarbenA {Rot, Blau, Grün};
enum class FarbenB {Gelb, Blau, Schwarz}; // Kein Problem hierBeim letzten Beispiel sehen Sie, dass bei der Verwendung von
enum Namenskonflikte auftreten können, während
enum class solche Konflikte verhindert. Dies macht
enum class sicherer und ist einer der Gründe, warum es in
modernem C++ bevorzugt wird.
Person, die private
Attribute für den Namen und das Alter einer Person hat. Die Klasse
sollte öffentliche Getter- und Setter-Methoden für diese Attribute
haben.Person-Klasse um einen
Standardkonstruktor, der keine Parameter akzeptiert, und einen
Parameterkonstruktor, der Name und Alter initialisiert.Person-Klasse einen Destruktor hinzu, der
eine Nachricht ausgibt, wenn ein Objekt zerstört wird.Student, die von der
Person-Klasse erbt. Student sollte ein
zusätzliches privates Attribut für die Matrikelnummer und eine
öffentliche Methode haben, um die Matrikelnummer zu erhalten und
festzulegen.Person-Klasse so, dass der Name
protected statt private ist. Überlegen Sie,
wie dies die Vererbung in der Student-Klasse
beeinflusst.Grundlegende Klasse
class Person {
private:
std::string name;
int age;
public:
void setName(const std::string& n) { name = n; }
void setAge(int a) { age = a; }
std::string getName() const { return name; }
int getAge() const { return age; }
};Konstruktor-Überladung
Person() : name(""), age(0) {}
Person(const std::string& n, int a) : name(n), age(a) {}Destruktor
~Person() {
std::cout << "Person " << name << " wurde zerstört." << std::endl;
}Vererbung
class Student : public Person {
private:
int matriculationNumber;
public:
void setMatriculationNumber(int m) { matriculationNumber = m; }
int getMatriculationNumber() const { return matriculationNumber; }
};Schutzmodifizierer
protected:
std::string name;Indem das name-Attribut auf protected
gesetzt wird, erhalten abgeleitete Klassen wie Student
direkten Zugriff darauf, ohne die Getter- oder Setter-Methoden verwenden
zu müssen.
staticstatic in C++ kann in verschiedenen Kontexten verwendet
werden: für Klassenmember, in Funktionen und für globale Variablen in
Dateien. Die folgenden Übungsaufgaben helfen Ihnen, dieses Schlüsselwort
besser zu verstehen:
Auto, die jedes Mal, wenn ein
neues Auto-Objekt erstellt wird, seine id um 1
erhöht. Verwenden Sie eine static-Variable, um dies zu
verfolgen.Auto-Klasse aus der vorherigen Aufgabe
eine static-Methode hinzu, die die aktuelle Anzahl von
erstellten Auto-Objekten zurückgibt.static-Variable innerhalb dieser
Funktion, um dies zu erreichen.static globalen
Variable und einer Funktion, die diese Variable manipuliert. Erstellen
Sie eine zweite Datei, die versucht, direkt auf diese Variable
zuzugreifen. Was beobachten Sie?Statische Klassenvariablen
class Auto {
private:
int id;
static int zaehler;
public:
Auto() {
id = zaehler++;
}
int getID() const {
return id;
}
};
int Auto::zaehler = 0;Statische Klassenmethoden
class Auto {
// ... (wie oben)
public:
static int getZaehler() {
return zaehler;
}
};Statische Funktionen in Funktionen
void zaehleAufrufe() {
static int aufrufe = 0;
aufrufe++;
std::cout << "Diese Funktion wurde " << aufrufe << " Mal aufgerufen." << std::endl;
}Statische globale Variablen
datei1.cpp:static int verborgeneVariable = 5;
int manipuliereVariable(int wert) {
verborgeneVariable += wert;
return verborgeneVariable;
}datei2.cpp:extern int verborgeneVariable; // Dies wird einen Kompilierungsfehler verursachen.Durch die Verwendung des Schlüsselworts static auf eine
globale Variable in datei1.cpp wird die Sichtbarkeit dieser
Variable auf diese Datei beschränkt, sodass sie nicht von anderen
Dateien, wie datei2.cpp, gesehen oder darauf zugegriffen
werden kann.
virtual und PolymorphieFahrzeug mit Attributen
wie marke und modell und zugehörigen Methoden.
Leiten Sie daraus eine Klasse Auto und eine Klasse
Motorrad ab.Fahrzeug eine Methode
anzeigen hinzu, die Details über das Fahrzeug ausgibt.
Überschreiben Sie diese Methode in den abgeleiteten Klassen
Auto und Motorrad. Erstellen Sie dann einen
Zeiger der Basisklasse, der auf ein Auto- oder
Motorrad-Objekt zeigt, und rufen Sie die Methode
anzeigen auf. Was beobachten Sie?virtual
anzeigen in der Basisklasse
Fahrzeug zu einer virtual-Methode. Wiederholen
Sie den Aufruf aus der vorherigen Übung. Was hat sich geändert?anzeigen in der Klasse
Fahrzeug zu einer reinen virtuellen Methode. Was bemerken
Sie über die Klasse Fahrzeug?virtual
Fahrzeug,
Auto und Motorrad hinzu. Warum könnte es
sinnvoll sein, den Destruktor in der Basisklasse virtual zu
machen?Grundlegende Vererbung
class Fahrzeug {
protected:
std::string marke;
std::string modell;
public:
// Getter und Setter...
};
class Auto : public Fahrzeug {
// Spezifische Eigenschaften und Methoden für Auto...
};
class Motorrad : public Fahrzeug {
// Spezifische Eigenschaften und Methoden für Motorrad...
};& 3. Polymorpher Methodenaufruf & Verwendung von
virtual
class Fahrzeug {
// ... wie oben
public:
virtual void anzeigen() {
std::cout << "Dies ist ein Fahrzeug der Marke " << marke << std::endl;
}
};
class Auto : public Fahrzeug {
// ... wie oben
public:
void anzeigen() override {
std::cout << "Dies ist ein Auto der Marke " << marke << std::endl;
}
};
class Motorrad : public Fahrzeug {
// ... wie oben
public:
void anzeigen() override {
std::cout << "Dies ist ein Motorrad der Marke " << marke << std::endl;
}
};
Fahrzeug* f = new Auto();
f->anzeigen(); // Gibt nun die spezifische Nachricht für ein Auto aus.
delete f;Reine virtuelle Methoden und abstrakte Klassen
class Fahrzeug {
// ... wie oben
public:
virtual void anzeigen() = 0; // Reine virtuelle Methode
};Die Klasse Fahrzeug ist nun eine abstrakte Klasse und
kann nicht instanziiert werden.
Destruktoren und virtual
class Fahrzeug {
// ... wie oben
public:
virtual ~Fahrzeug() {
std::cout << "Fahrzeug-Destruktor aufgerufen!" << std::endl;
}
};Ein virtual-Destruktor stellt sicher, dass beim Löschen
eines Zeigers auf ein Basisobjekt der richtige abgeleitete Destruktor
aufgerufen wird.
Zahl, die eine ganze Zahl
(int) als Attribut hat. Überladen Sie den +
Operator, um zwei Zahl-Objekte zu addieren und ein neues
Zahl-Objekt zurückzugeben.Zahl-Klasse, indem Sie die Operatoren
== und != überladen, um zwei
Zahl-Objekte zu vergleichen.>>) und Ausgabeoperatoren
(<<) für die Zahl-Klasse, um Objekte
dieser Klasse einfach einlesen und ausgeben zu können.Vektor für einen
zweidimensionalen Vektor mit den Attributen x und
y. Überladen Sie den Zuweisungsoperator (=)
und implementieren Sie eine tiefe Kopie.Vektor-Klasse um die Operatoren
+, -, * (Skalarprodukt) und
/ (Skalarmultiplikation).Einfache Operatorüberladung
class Zahl {
private:
int wert;
public:
Zahl(int w) : wert(w) {}
Zahl operator+(const Zahl& z) {
return Zahl(wert + z.wert);
}
};Vergleichsoperatoren
bool operator==(const Zahl& z) const {
return wert == z.wert;
}
bool operator!=(const Zahl& z) const {
return !(*this == z);
}Ein- und Ausgabeoperatoren
friend std::istream& operator>>(std::istream& in, Zahl& z) {
in >> z.wert;
return in;
}
friend std::ostream& operator<<(std::ostream& out, const Zahl& z) {
out << z.wert;
return out;
}Zuweisungsoperator
class Vektor {
public:
double x, y;
Vektor& operator=(const Vektor& v) {
if (this != &v) {
x = v.x;
y = v.y;
}
return *this;
}
};Erweiterte arithmetische Operatoren
Vektor operator+(const Vektor& v) const {
return Vektor(x + v.x, y + v.y);
}
Vektor operator-(const Vektor& v) const {
return Vektor(x - v.x, y - v.y);
}
double operator*(const Vektor& v) const {
return x * v.x + y * v.y;
}
Vektor operator/(double scalar) const {
return Vektor(x / scalar, y / scalar);
}RAII ist ein Programmierparadigma in C++, bei dem Ressourcen, die während der Lebensdauer eines Objekts benötigt werden, während der Initialisierung des Objekts erworben und beim Zerstören des Objekts freigegeben werden. Es wird oft in Verbindung mit Ressourcen wie Speicher, Dateihandles oder Netzwerksockets verwendet.
SpeicherManager, die im
Konstruktor Speicher mit new reserviert und im Destruktor
diesen Speicher mit delete wieder freigibt.Datei, die im
Konstruktor eine Datei öffnet und im Destruktor die Datei schließt.
Implementieren Sie Methoden zum Lesen und Schreiben in die Datei.std::unique_ptr bietet.Einfacher Speichermanager
class SpeicherManager {
int* daten;
public:
SpeicherManager(size_t groesse) {
daten = new int[groesse];
}
~SpeicherManager() {
delete[] daten;
}
};Datei-Wrapper
#include <fstream>
class Datei {
std::fstream dateiStream;
public:
Datei(const std::string& pfad) : dateiStream(pfad, std::ios::in | std::ios::out) {}
~Datei() {
if (dateiStream.is_open()) {
dateiStream.close();
}
}
// Weitere Methoden zum Lesen und Schreiben...
};Mutex-Wrapper
#include <mutex>
class MutexWrapper {
std::mutex& m;
public:
MutexWrapper(std::mutex& mutex) : m(mutex) {
m.lock();
}
~MutexWrapper() {
m.unlock();
}
};Smart Pointer
template <typename T>
class MeinSmartPointer {
T* ptr;
public:
MeinSmartPointer(T* p) : ptr(p) {}
~MeinSmartPointer() {
delete ptr;
}
T& operator*() { return *ptr; }
T* operator->() { return ptr; }
};Dynamisches Array
class DynArray {
int* daten;
size_t groesse;
public:
DynArray(size_t g) : groesse(g) {
daten = new int[groesse];
}
~DynArray() {
delete[] daten;
}
// Weitere Methoden, um auf das Array zuzugreifen...
};Die Ausnahmebehandlung ist ein Mechanismus, um zur Laufzeit auftretende Probleme oder unerwartete Situationen zu behandeln und dabei eine saubere und geordnete Ressourcenfreigabe zu gewährleisten.
std::string zurückgibt. Werfen Sie
eine Ausnahme, wenn die Datei nicht gefunden wird oder nicht geöffnet
werden kann.MeineAusnahme und werfen Sie diese in einer Funktion, wenn
eine bestimmte Bedingung erfüllt ist (z.B. negative Zahl). Fangen Sie
die Ausnahme in der main-Funktion ab und geben Sie eine
benutzerfreundliche Fehlermeldung aus.Einfache Division
double teile(int zaehler, int nenner) {
if(nenner == 0) throw std::runtime_error("Teilen durch 0 ist nicht erlaubt!");
return static_cast<double>(zaehler) / nenner;
}Dateizugriff
#include <fstream>
#include <sstream>
std::string leseDatei(const std::string& pfad) {
std::ifstream datei(pfad);
if(!datei.is_open()) throw std::runtime_error("Datei konnte nicht geöffnet werden!");
std::stringstream ss;
ss << datei.rdbuf();
return ss.str();
}Dynamisches Array
class DynArray {
private:
int* daten;
size_t groesse;
public:
// Konstruktor, Destruktor, ...
int& at(size_t index) {
if(index >= groesse) throw std::out_of_range("Ungültiger Index!");
return daten[index];
}
};Benutzerdefinierte Ausnahme
class MeineAusnahme : public std::exception {
public:
const char* what() const noexcept override {
return "Eine benutzerdefinierte Ausnahme wurde geworfen!";
}
};
void testeFunktion(int zahl) {
if(zahl < 0) throw MeineAusnahme();
// weitere Logik...
}
int main() {
try {
testeFunktion(-5);
} catch(const MeineAusnahme& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}Ressourcenverwaltung
class RessourcenManager {
private:
int* ressource;
public:
RessourcenManager(size_t groesse) {
ressource = new int[groesse];
if(!ressource) throw std::runtime_error("Ressource konnte nicht erworben werden!");
}
~RessourcenManager() {
delete[] ressource;
}
};Templates in C++ bieten die Möglichkeit, generischen Code für verschiedene Datentypen zu schreiben. Hier sind einige Übungsaufgaben, um Ihr Wissen über Templates zu vertiefen:
int, double und std::string.Stack, die
Methoden zum Hinzufügen (push) und Entfernen
(pop) von Elementen sowie zum Anzeigen des obersten
Elements (top) bietet.int, double) zulässig ist.
Nutzen Sie hierfür static_assert mit den Typ-Traits aus dem
Header <type_traits>.bool Datentyp,
die Speicher effizienter nutzt (bitweise Speicherung).Generischer Swap
template <typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}Generische Klasse Stack
template <typename T>
class Stack {
private:
std::vector<T> elements;
public:
void push(const T& elem) { elements.push_back(elem); }
void pop() { elements.pop_back(); }
T& top() { return elements.back(); }
};Begrenzte Template-Klassen
#include <type_traits>
template <typename T>
class Numeric {
static_assert(std::is_arithmetic<T>::value, "Nur numerische Datentypen erlaubt!");
T value;
};Generische Summenfunktion
template <typename Container>
auto sum(const Container& c) -> decltype(*c.begin() + *c.begin()) {
using T = decltype(*c.begin() + *c.begin());
T result = T();
for (const auto& elem : c) {
result += elem;
}
return result;
}Spezialisierung
template <typename T>
class Vektor {
std::vector<T> data;
// ...
};
template <>
class Vektor<bool> {
std::vector<uint8_t> data;
// Implementieren Sie die Speicherung bitweise
};Lambda-Ausdrücke sind anonyme Funktionen, die in C++ zum Schreiben von kurzen, in den Code eingebetteten Funktionen verwendet werden können. Hier sind einige Übungsaufgaben, um Ihr Wissen über Lambda-Ausdrücke zu vertiefen:
[=], [&], [x, &y] usw.)
und beobachten Sie das Verhalten.Grundlegende Lambda
auto add = [](int a, int b) -> int {
return a + b;
};
std::cout << add(3, 4); // Ausgabe: 7Lambda mit Capture
int value = 10;
auto multiply = [value](int a) -> int {
return a * value;
};
std::cout << multiply(5); // Ausgabe: 50Lambda als Funktionsargument
std::vector<int> filter(const std::vector<int>& values, std::function<bool(int)> predicate) {
std::vector<int> result;
for (int value : values) {
if (predicate(value)) {
result.push_back(value);
}
}
return result;
}
std::vector<int> numbers = {1, 2, 3, 4, 5};
auto even = [](int n) { return n % 2 == 0; };
auto evenNumbers = filter(numbers, even);Rückgabe von Lambda
auto multiplier(int factor) {
return [factor](int x) -> int {
return x * factor;
};
}
auto doubleIt = multiplier(2);
std::cout << doubleIt(5); // Ausgabe: 10Lambda mit mehreren Capture-Varianten
int x = 5, y = 7;
auto byValue = [=]() { return x + y; };
auto byReference = [&]() { y *= 2; return x + y; };
auto mixedCapture = [x, &y]() { y += x; return y; };
std::cout << byValue() << std::endl; // Ausgabe: 12
std::cout << byReference() << std::endl; // Ausgabe: 19 (y wird zu 14 verdoppelt)
std::cout << mixedCapture() << std::endl; // Ausgabe: 19 (y wird zu 12 durch Hinzufügen von x)