C++ Spickzettel

C++ mit praxisnahen Labs lernen

Lernen Sie die C++-Programmierung durch praxisnahe Labs und reale Szenarien. LabEx bietet umfassende C++-Kurse, die wesentliche Syntax, objektorientierte Programmierung, STL-Container, Speicherverwaltung und fortgeschrittene Techniken abdecken. Meistern Sie die leistungsstarken Funktionen von C++, um Hochleistungsanwendungen und Systemsoftware zu erstellen.

Grundlegende Syntax & Struktur

Hallo Welt Programm

Grundstruktur eines C+±Programms.

#include <iostream>
using namespace std;
int main() {
    cout << "Hallo, Welt!" << endl;
    return 0;
}

Header und Namespaces

Bibliotheken einbinden und Namespaces verwalten.

#include <iostream>  // Eingabe-/Ausgabestream
#include <vector>    // Dynamische Arrays
#include <string>    // String-Klasse
#include <algorithm> // STL-Algorithmen
using namespace std;
// Oder einzeln angeben:
// using std::cout;
// using std::cin;

Kommentare

Einzeilige und mehrzeilige Kommentare.

// Einzeiliger Kommentar
/*
Mehrzeiliger Kommentar
erstreckt sich über mehrere Zeilen
*/
// TODO: Funktion implementieren
/* FIXME: Bug in diesem Abschnitt */

Main Funktion

Einstiegspunkt des Programms mit Rückgabewerten.

int main() {
    // Programmcode hier
    return 0;  // Erfolg
}
int main(int argc, char* argv[]) {
    // argc: Argumentanzahl
    // argv: Argumentwerte (Kommandozeile)
    return 0;
}
Quiz

Melden Sie sich an, um dieses Quiz zu beantworten und Ihren Lernfortschritt zu verfolgen

Was ist der Unterschied zwischen C- und C++-Ausgabenanweisungen?
Es gibt keinen Unterschied
C verwendet printf(), C++ verwendet cout mit dem << Operator
C++ unterstützt keine Ausgabe
C verwendet cout, C++ verwendet printf

Einfache Ausgabe

Text und Variablen auf der Konsole anzeigen.

cout << "Hallo" << endl;
cout << "Wert: " << 42 << endl;
// Mehrere Werte in einer Zeile
cout << "Name: " << name << ", Alter: " << age << endl;

Einfache Eingabe

Benutzereingaben von der Konsole lesen.

int age;
string name;
cin >> age;
cin >> name;
// Ganze Zeile inklusive Leerzeichen lesen
getline(cin, name);

Datentypen & Variablen

Primitive Typen

Grundlegende Datentypen zur Speicherung verschiedener Wertarten.

// Integer-Typen
int age = 25;
short small_num = 100;
long large_num = 1000000L;
long long huge_num = 9223372036854775807LL;
// Gleitkommatypen
float price = 19.99f;
double precise = 3.14159265359;
// Zeichen und Boolescher Wert
char grade = 'A';
bool is_valid = true;

String & Arrays

Text- und Sammlungstypen.

// Strings
string name = "John Doe";
string empty_str;
// Arrays
int numbers[5] = {1, 2, 3, 4, 5};
int matrix[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
// Dynamische Arrays (Vektoren)
vector<int> dynamic_array = {10, 20, 30};
vector<string> names(5); // Größe 5, leere Strings
Quiz

Melden Sie sich an, um dieses Quiz zu beantworten und Ihren Lernfortschritt zu verfolgen

Was ist der Hauptvorteil von vector gegenüber normalen Arrays in C++?
Vektoren sind schneller
Vektoren können dynamisch in der Größe geändert werden, während Arrays eine feste Größe haben
Vektoren verbrauchen weniger Speicher
Es gibt keinen Vorteil

Konstanten & Auto

Unveränderliche Werte und automatische Typableitung.

// Konstanten
const int MAX_SIZE = 100;
const double PI = 3.14159;
// Auto-Schlüsselwort (C++11+)
auto x = 42;        // int
auto y = 3.14;      // double
auto name = "John"; // const char*
// Typ-Aliase
typedef unsigned int uint;
using real = double;

Kontrollflussstrukturen

Bedingte Anweisungen

Entscheidungen basierend auf Bedingungen treffen.

// If-else-Anweisung
if (age >= 18) {
    cout << "Erwachsen" << endl;
} else if (age >= 13) {
    cout << "Teenager" << endl;
} else {
    cout << "Kind" << endl;
}
// Ternärer Operator
string status = (age >= 18) ? "Erwachsen" : "Minderjährig";
// Switch-Anweisung
switch (grade) {
    case 'A':
        cout << "Ausgezeichnet!" << endl;
        break;
    case 'B':
        cout << "Gut gemacht!" << endl;
        break;
    default:
        cout << "Weiter versuchen!" << endl;
}

For-Schleifen

Iteration mit zählerbasierten Schleifen.

// Traditionelle for-Schleife
for (int i = 0; i < 10; i++) {
    cout << i << " ";
}
// Bereichsbasierte for-Schleife (C++11+)
vector<int> numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
    cout << num << " ";
}
// Auto mit bereichsbasierter Schleife
for (auto& item : container) {
    // Element verarbeiten
}
Quiz

Melden Sie sich an, um dieses Quiz zu beantworten und Ihren Lernfortschritt zu verfolgen

Was ist eine bereichsbasierte for-Schleife in C++?
Eine Schleife, die nur mit Arrays funktioniert
Eine Schleife, die automatisch über alle Elemente in einem Container iteriert
Eine Schleife, die ewig läuft
Eine Schleife, die eine manuelle Indexverwaltung erfordert

While-Schleifen

Zustandsbasierte Iteration.

// While-Schleife
int count = 0;
while (count < 5) {
    cout << count << endl;
    count++;
}
// Do-while-Schleife (wird mindestens einmal ausgeführt)
int input;
do {
    cout << "Geben Sie eine Zahl ein (0 zum Beenden): ";
    cin >> input;
} while (input != 0);

Schleifenkontrolle

Break- und Continue-Anweisungen.

for (int i = 0; i < 10; i++) {
    if (i == 3) {
        continue; // Iteration überspringen
    }
    if (i == 7) {
        break;    // Schleife verlassen
    }
    cout << i << " ";
}
// Geschachtelte Schleifen mit beschriftetem Break
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i == j) break; // Nur innere Schleife beenden
        cout << i << "," << j << " ";
    }
}

Funktionen

Funktionsdeklaration & Definition

Wiederverwendbare Codeblöcke erstellen.

// Funktionsdeklaration (Prototyp)
int add(int a, int b);
void printMessage(string msg);
// Funktionsdefinition
int add(int a, int b) {
    return a + b;
}
void printMessage(string msg) {
    cout << msg << endl;
}
// Funktionsaufruf
int result = add(5, 3);
printMessage("Hallo, Funktionen!");

Funktionsüberladung

Mehrere Funktionen mit demselben Namen.

// Unterschiedliche Parametertypen
int multiply(int a, int b) {
    return a * b;
}
double multiply(double a, double b) {
    return a * b;
}
// Unterschiedliche Anzahl von Parametern
int multiply(int a, int b, int c) {
    return a * b * c;
}

Standardparameter

Standardwerte für Funktionsparameter bereitstellen.

void greet(string name, string greeting = "Hallo") {
    cout << greeting << ", " << name << "!" << endl;
}
// Funktionsaufrufe
greet("Alice");              // Verwendet Standard "Hallo"
greet("Bob", "Guten Morgen"); // Verwendet benutzerdefinierte Begrüßung

Übergabe per Referenz

Variablen über Funktionsparameter modifizieren.

// Übergabe per Wert (Kopie)
void changeValue(int x) {
    x = 100; // Originalvariable unverändert
}
// Übergabe per Referenz
void changeReference(int& x) {
    x = 100; // Originalvariable modifiziert
}
// Konstante Referenz (schreibgeschützt, effizient)
void processLargeData(const vector<int>& data) {
    // Daten lesen, aber nicht modifizieren
}

Objektorientierte Programmierung

Klassendefinition

Benutzerdefinierte Datentypen mit Attributen und Methoden definieren.

class Rectangle {
private:
    double width, height;
public:
    // Konstruktor
    Rectangle(double w, double h) : width(w), height(h) {}

    // Standardkonstruktor
    Rectangle() : width(0), height(0) {}

    // Memberfunktionen
    double area() const {
        return width * height;
    }

    void setDimensions(double w, double h) {
        width = w;
        height = h;
    }

    // Getter-Funktionen
    double getWidth() const { return width; }
    double getHeight() const { return height; }
};

Objekterstellung & Verwendung

Klassenobjekte instanziieren und verwenden.

// Objekte erstellen
Rectangle rect1(5.0, 3.0);
Rectangle rect2; // Standardkonstruktor
// Memberfunktionen verwenden
cout << "Fläche: " << rect1.area() << endl;
rect2.setDimensions(4.0, 2.0);
// Dynamische Allokation
Rectangle* rect3 = new Rectangle(6.0, 4.0);
cout << rect3->area() << endl;
delete rect3; // Speicher freigeben

Vererbung

Spezialisierte Klassen aus Basisklassen erstellen.

class Shape {
protected:
    string color;

public:
    Shape(string c) : color(c) {}
    virtual double area() const = 0; // Reine virtuelle Funktion
    string getColor() const { return color; }
};
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r, string c) : Shape(c), radius(r) {}

    double area() const override {
        return 3.14159 * radius * radius;
    }
};

Polymorphismus

Basisklassenzeiger verwenden, um abgeleitete Objekte abzurufen.

// Virtuelle Funktionen und Polymorphismus
vector<Shape*> shapes;
shapes.push_back(new Circle(5.0, "rot"));
shapes.push_back(new Rectangle(4.0, 6.0));
for (Shape* shape : shapes) {
    cout << "Fläche: " << shape->area() << endl;
    // Ruft die entsprechende abgeleitete Klassenmethode auf
}

Speicherverwaltung

Dynamische Speicherzuweisung

Speicher zur Laufzeit zuweisen und freigeben.

// Einzelnes Objekt
int* ptr = new int(42);
cout << *ptr << endl;
delete ptr;
ptr = nullptr;
// Array-Zuweisung
int* arr = new int[10];
for (int i = 0; i < 10; i++) {
    arr[i] = i * i;
}
delete[] arr;
// Prüfen auf Zuweisungsfehler
int* large_array = new(nothrow) int[1000000];
if (large_array == nullptr) {
    cout << "Zuweisung fehlgeschlagen!" << endl;
}

Smart Pointers (C++11+)

Automatische Speicherverwaltung mit RAII.

#include <memory>
// unique_ptr (exklusives Eigentum)
unique_ptr<int> ptr1 = make_unique<int>(42);
unique_ptr<int> ptr2 = move(ptr1); // Eigentum übertragen
// shared_ptr (geteiltes Eigentum)
shared_ptr<int> sptr1 = make_shared<int>(100);
shared_ptr<int> sptr2 = sptr1; // Eigentum teilen
cout << sptr1.use_count() << endl; // Referenzzähler

Referenzen vs Zeiger

Zwei Wege, um indirekt auf Objekte zuzugreifen.

int x = 10;
// Referenz (Alias)
int& ref = x;  // Muss initialisiert werden
ref = 20;      // Ändert x zu 20
// Zeiger
int* ptr = &x; // Zeigt auf die Adresse von x
*ptr = 30;     // Dereferenzieren und x ändern
ptr = nullptr; // Kann auf nichts zeigen
// Const-Varianten
const int* ptr1 = &x;    // Wert kann nicht geändert werden
int* const ptr2 = &x;    // Adresse kann nicht geändert werden
const int* const ptr3 = &x; // Keines von beiden kann geändert werden

Stack vs Heap

Speicherzuweisungsstrategien.

// Stack-Zuweisung (automatisch)
int stack_var = 42;
int stack_array[100];
// Heap-Zuweisung (dynamisch)
int* heap_var = new int(42);
int* heap_array = new int[100];
// Stack-Objekte werden automatisch bereinigt
// Heap-Objekte müssen manuell gelöscht werden
delete heap_var;
delete[] heap_array;

Standard Template Library (STL)

Container: Vector & String

Dynamische Arrays und String-Manipulation.

#include <vector>
#include <string>
// Vektor-Operationen
vector<int> nums = {1, 2, 3};
nums.push_back(4);        // Element hinzufügen
nums.pop_back();          // Letztes Element entfernen
nums.insert(nums.begin() + 1, 10); // An Position einfügen
nums.erase(nums.begin()); // Erstes Element entfernen
// String-Operationen
string text = "Hallo";
text += " Welt";         // Konkatenation
text.append("!");         // Anhängen
cout << text.substr(0, 5) << endl; // Substring
text.replace(6, 5, "C++"); // "Welt" durch "C++" ersetzen

Container: Map & Set

Assoziative Container für Schlüssel-Wert-Paare und eindeutige Elemente.

#include <map>
#include <set>
// Map (Schlüssel-Wert-Paare)
map<string, int> ages;
ages["Alice"] = 25;
ages["Bob"] = 30;
ages.insert({"Charlie", 35});
// Set (eindeutige Elemente)
set<int> unique_nums = {3, 1, 4, 1, 5, 9};
unique_nums.insert(2);
unique_nums.erase(1);
// Automatisch sortiert: {2, 3, 4, 5, 9}

Algorithmen

STL-Algorithmen für gängige Operationen.

#include <algorithm>
vector<int> nums = {64, 34, 25, 12, 22, 11, 90};
// Sortieren
sort(nums.begin(), nums.end());
sort(nums.rbegin(), nums.rend()); // Absteigend sortieren
// Suchen
auto it = find(nums.begin(), nums.end(), 25);
if (it != nums.end()) {
    cout << "Gefunden an Position: " << it - nums.begin();
}
// Andere nützliche Algorithmen
reverse(nums.begin(), nums.end());
int max_val = *max_element(nums.begin(), nums.end());
int count = count_if(nums.begin(), nums.end(),
                    [](int x) { return x > 50; });

Iteratoren

Effiziente Navigation durch Container.

vector<string> words = {"hallo", "welt", "cpp"};
// Iteratortypen
vector<string>::iterator it;
auto it2 = words.begin(); // C++11 auto
// Durch Container iterieren
for (it = words.begin(); it != words.end(); ++it) {
    cout << *it << " ";
}
// Bereichsbasierte Schleife (bevorzugt)
for (const auto& word : words) {
    cout << word << " ";
}

Eingabe-/Ausgabeoperationen

Dateieingabe: Dateien lesen

Daten aus Textdateien lesen.

#include <fstream>
#include <sstream>
// Ganze Datei lesen
ifstream file("data.txt");
if (file.is_open()) {
    string line;
    while (getline(file, line)) {
        cout << line << endl;
    }
    file.close();
}
// Wort für Wort lesen
ifstream file2("numbers.txt");
int number;
while (file2 >> number) {
    cout << number << " ";
}
// Mit Fehlerprüfung
if (!file.good()) {
    cerr << "Fehler beim Lesen der Datei!" << endl;
}

String Stream Verarbeitung

Strings als Streams parsen und manipulieren.

#include <sstream>
// Kommagetrennte Werte parsen
string data = "apfel,banane,kirsche";
stringstream ss(data);
string item;
vector<string> fruits;
while (getline(ss, item, ',')) {
    fruits.push_back(item);
}
// Strings in Zahlen umwandeln
string num_str = "123";
int num = stoi(num_str);
double d = stod("3.14159");
string back_to_str = to_string(num);

Dateiausgabe: Dateien schreiben

Daten in Textdateien schreiben.

// In Datei schreiben
ofstream outfile("output.txt");
if (outfile.is_open()) {
    outfile << "Hallo, Datei!" << endl;
    outfile << "Zahl: " << 42 << endl;
    outfile.close();
}
// An bestehende Datei anhängen
ofstream appendfile("log.txt", ios::app);
appendfile << "Neuer Logeintrag" << endl;
// Vektor in Datei schreiben
vector<int> numbers = {1, 2, 3, 4, 5};
ofstream numfile("numbers.txt");
for (int num : numbers) {
    numfile << num << " ";
}

Stream-Formatierung

Ausgabeformat und Präzision steuern.

#include <iomanip>
double pi = 3.14159265;
cout << fixed << setprecision(2) << pi << endl; // 3.14
cout << setw(10) << "Rechts" << endl;          // Rechtsbündig
cout << left << setw(10) << "Links" << endl;     // Linksbündig
cout << hex << 255 << endl;                    // Hexadezimal: ff

Fehlerbehandlung

Try-Catch-Blöcke

Ausnahmen behandeln, die während der Ausführung auftreten können.

try {
    int result = 10 / 0; // Dies könnte eine Ausnahme auslösen
    vector<int> vec(5);
    vec.at(10) = 100;    // Zugriff außerhalb der Grenzen

} catch (const exception& e) {
    cout << "Ausnahme abgefangen: " << e.what() << endl;
} catch (...) {
    cout << "Unbekannte Ausnahme abgefangen!" << endl;
}
// Spezifische Ausnahme-Typen
try {
    string str = "abc";
    int num = stoi(str); // Löst invalid_argument aus
} catch (const invalid_argument& e) {
    cout << "Ungültiges Argument: " << e.what() << endl;
} catch (const out_of_range& e) {
    cout << "Außerhalb des Bereichs: " << e.what() << endl;
}

Benutzerdefinierte Ausnahmen werfen

Eigene Ausnahmen erstellen und auslösen.

// Benutzerdefinierte Ausnahme-Klasse
class CustomException : public exception {
    string message;
public:
    CustomException(const string& msg) : message(msg) {}
    const char* what() const noexcept override {
        return message.c_str();
    }
};
// Funktion, die Ausnahme auslöst
void validateAge(int age) {
    if (age < 0 || age > 150) {
        throw CustomException("Ungültiger Altersbereich!");
    }
}
// Verwendung
try {
    validateAge(-5);
} catch (const CustomException& e) {
    cout << e.what() << endl;
}

RAII-Muster

Resource Acquisition Is Initialization für sicheres Ressourcenmanagement.

// RAII mit Smart Pointern
{
    unique_ptr<int[]> arr = make_unique<int[]>(1000);
    // Array wird automatisch gelöscht, wenn es den Gültigkeitsbereich verlässt
}
// RAII mit Dateihandhabung
{
    ifstream file("data.txt");
    // Datei wird automatisch geschlossen, wenn sie den Gültigkeitsbereich verlässt
    if (file.is_open()) {
        // Datei verarbeiten
    }
}
// Benutzerdefinierte RAII-Klasse
class FileHandler {
    FILE* file;
public:
    FileHandler(const char* filename) {
        file = fopen(filename, "r");
    }
    ~FileHandler() {
        if (file) fclose(file);
    }
    FILE* get() { return file; }
};

Assertions & Debugging

Annahmen des Programms debuggen und validieren.

#include <cassert>
#include <iostream>
void processArray(int* arr, int size) {
    assert(arr != nullptr);  // Debug-Assertion
    assert(size > 0);        // Annahme validieren

    // Array verarbeiten...
}
// Bedingte Kompilierung für Debug-Ausgabe
#ifdef DEBUG
    #define DBG_PRINT(x) cout << "DEBUG: " << x << endl
#else
    #define DBG_PRINT(x)
#endif
// Verwendung
DBG_PRINT("Funktion wird gestartet");

Kompilierung & Build-Prozess

GCC/G++ Kompilierung

GNU Compiler Collection für C++.

# Einfache Kompilierung
g++ -o program main.cpp
# Mit Debugging-Informationen
g++ -g -o program main.cpp
# Optimierungsstufen
g++ -O2 -o program main.cpp
# Mehrere Quelldateien
g++ -o program main.cpp utils.cpp math.cpp
# Zusätzliche Verzeichnisse einbinden
g++ -I/usr/local/include -o program main.cpp
# Bibliotheken verknüpfen
g++ -o program main.cpp -lm -lpthread

Moderne C++ Standards

Kompilieren mit spezifischen C+±Standardversionen.

# C++11 Standard
g++ -std=c++11 -o program main.cpp
# C++14 Standard
g++ -std=c++14 -o program main.cpp
# C++17 Standard (empfohlen)
g++ -std=c++17 -o program main.cpp
# C++20 Standard (neueste)
g++ -std=c++20 -o program main.cpp
# Alle Warnungen aktivieren
g++ -Wall -Wextra -std=c++17 -o program main.cpp

Makefile Grundlagen

Kompilierung mit dem make-Dienstprogramm automatisieren.

# Einfaches Makefile
CXX = g++
CXXFLAGS = -std=c++17 -Wall -g
TARGET = program
SOURCES = main.cpp utils.cpp
$(TARGET): $(SOURCES)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SOURCES)
clean:
rm -f $(TARGET)
.PHONY: clean

Best Practices & Tipps

Benennungskonventionen

Konsistente Benennung macht Code lesbarer.

// Variablen und Funktionen: snake_case oder camelCase
int student_count;
int studentCount;
void calculateAverage();
// Konstanten: UPPER_CASE
const int MAX_BUFFER_SIZE = 1024;
const double PI = 3.14159;
// Klassen: PascalCase
class StudentRecord {
    // Membervariablen: Präfix mit m_ oder Suffix _
    string m_name;
    int age_;

public:
    // Öffentliche Schnittstelle
    void setName(const string& name);
    string getName() const;
};

Speichersicherheit

Häufige speicherbezogene Fehler vermeiden.

// Smart Pointer anstelle von Rohzeigern verwenden
auto ptr = make_unique<int>(42);
auto shared = make_shared<vector<int>>(10);
// Variablen initialisieren
int count = 0;        // Gut
int count;            // Gefährlich - nicht initialisiert
// Bereichsbasierte Schleifen sind sicherer
for (const auto& item : container) {
    // Element sicher verarbeiten
}
// Zeigergültigkeit prüfen
if (ptr != nullptr) {
    // Sicher dereferenzierbar
}

Performance-Tipps

Effizienten C+±Code schreiben.

// Große Objekte per const Referenz übergeben
void processData(const vector<int>& data) {
    // Kopieren großer Objekte vermeiden
}
// Prä-Inkrement für Iteratoren verwenden
for (auto it = vec.begin(); it != vec.end(); ++it) {
    // ++it ist oft schneller als it++
}
// Vektor-Kapazität reservieren, wenn die Größe bekannt ist
vector<int> numbers;
numbers.reserve(1000); // Neuallokationen vermeiden
// emplace anstelle von push für Objekte verwenden
vector<string> words;
words.emplace_back("Hallo"); // In-Place konstruieren
words.push_back(string("Welt")); // Konstruieren und dann kopieren

Code-Organisation

Code für Wartbarkeit strukturieren.

// Header-Datei (utils.h)
#ifndef UTILS_H
#define UTILS_H
class MathUtils {
public:
    static double calculateArea(double radius);
    static int fibonacci(int n);
};
#endif // UTILS_H
// Implementierungsdatei (utils.cpp)
#include "utils.h"
#include <cmath>
double MathUtils::calculateArea(double radius) {
    return M_PI * radius * radius;
}
// Memberfunktionen nach Möglichkeit als const deklarieren
double getRadius() const { return radius; }