C mit Klassen, Templates und moderner Standardbibliothek
Ausführen: Datei hallo.cpp speichern, mit g++ -std=c++20 -Wall -Wextra hallo.cpp -o hallo übersetzen und mit ./hallo starten. Auch clang++ und Visual Studio (MSVC) funktionieren.
-std=c++20std::cout << ... gibt aus, #include <iostream> bindet es einstd::string, std::vector und Smart Pointer statt roher Zeiger#include <iostream>
int main() {
std::cout << "Hallo, Welt!" << std::endl;
return 0;
}std::string, auto, const; Initialisierung mit {} verhindert Datenverluststd::cout, std::format (C++20); Eingabe: std::cin, std::getline&) sind Alias-Namen, const& für große Parameternamespace) verhindern Namenskonflikteusing namespace std; in Headern#include <iostream>
#include <string>
#include <format> // C++20
int main() {
int alter = 17;
double preis = 19.99;
char buchstabe = 'A';
bool fertig = true;
std::string name = "Mia";
auto zahl = 42; // auto: Typ wird erkannt (int)
const double PI = 3.14159;
std::cout << name << " ist " << alter << ", Preis " << preis << ", " << buchstabe << ", " << fertig << '\n';
std::cout << std::boolalpha << fertig << ' ' << std::noboolalpha << zahl << ' ' << PI << '\n';
std::cout << std::format("{} hat {} Punkte ({:.1f}%)\n", name, 42, 87.5);
std::cout << std::format("|{:>6}|{:<6}|{:^6}|{:06.2f}|\n", "ab", "cd", "ef", 3.14159);
return 0;
}if (init; bedingung) begrenzt Hilfsvariablen auf den Blockfor (const auto& x : container) ist der Standard; Structured Bindings zerlegen Paarebreak, continue wie in Csort, accumulate, count_if) ersetzen oft handgeschriebene Schleifensize_t-Vergleichen vorsichtig schreiben (vorzeichenlos!)#include <iostream>
int main() {
int punkte = 73;
if (punkte >= 90) std::cout << "sehr gut\n";
else if (punkte >= 75) std::cout << "gut\n";
else if (punkte >= 50) std::cout << "bestanden\n";
else std::cout << "nicht bestanden\n";
// if mit Initialisierung (C++17): Variable gilt nur im if-Block
if (int rest = punkte % 10; rest == 3) std::cout << "endet auf 3\n";
switch (punkte / 25) {
case 0: case 1: std::cout << "unter 50\n"; break;
case 2: std::cout << "50 bis 74\n"; break;
default: std::cout << "ab 75\n";
}
std::cout << (punkte >= 50 ? "ok" : "nicht ok") << '\n';
return 0;
}const&struct oder std::tuple mit Structured Bindings[Capture](Parameter) { Rumpf }; [=] kopiert, [&] verweistconstexpr rechnet zur Übersetzungszeit, static_assert prüft dortstd::function speichert beliebige aufrufbare Objekte#include <iostream>
#include <string>
int addiere(int a, int b) { return a + b; }
double potenz(double basis, int exponent = 2) { // Standardwert
double r = 1;
for (int i = 0; i < exponent; i++) r *= basis;
return r;
}
// Überladen: gleicher Name, andere Parameter
std::string beschreibe(int x) { return "int " + std::to_string(x); }
std::string beschreibe(double x) { return "double " + std::to_string(x).substr(0, 4); }
std::string beschreibe(const std::string& x) { return "string " + x; }
// Mehrere Rückgabewerte über struct oder Tupel
struct Ergebnis { int quotient, rest; };
Ergebnis teile(int a, int b) { return {a / b, a % b}; }
int main() {
std::cout << addiere(3, 4) << ' ' << potenz(3) << ' ' << potenz(2, 10) << '\n';
std::cout << beschreibe(5) << ", " << beschreibe(2.5) << ", " << beschreibe(std::string("hi")) << '\n';
auto [q, r] = teile(17, 5);
std::cout << q << " Rest " << r << '\n';
return 0;
}std::string verwaltet Text selbst; std::string_view ist eine kopierfreie Sichtstd::vector ist der Standardcontainer: push_back, size, at, erasemap/set sortiert, unordered_map/unordered_set per Hashmap[key] legt fehlende Schlüssel an: find/contains zum Suchenbegin(), end()) verbinden Container mit Algorithmen#include <iostream>
#include <string>
#include <string_view>
#include <algorithm>
int main() {
std::string s = "Hallo Welt";
s += "!";
std::cout << s << " " << s.size() << " " << s[0] << " " << s.at(1) << '\n';
std::cout << s.substr(6, 4) << " " << s.find("Welt") << " " << (s.find("x") == std::string::npos) << '\n';
s.replace(0, 5, "Servus");
s.insert(s.size(), "!!");
std::cout << s << '\n';
std::string a = "abc", b = "abd";
std::cout << (a < b) << (a == "abc") << ' ' << a + b << '\n';
std::transform(a.begin(), a.end(), a.begin(), [](unsigned char c) { return std::toupper(c); });
std::cout << a << ' ' << std::to_string(42) + "x" << ' ' << std::stoi("123") + 1 << ' ' << std::stod("2.5") * 2 << '\n';
std::string_view sicht = s; // nur ansehen, nicht kopieren
std::cout << sicht.substr(0, 6) << " " << s.starts_with("Servus") << '\n'; // C++20
return 0;
}public/private; Konstruktor mit Initialisierungslisteconst-Methoden ändern das Objekt nichtstd::move verschiebt statt zu kopieren+, ==, <<)#include <iostream>
#include <string>
class Konto {
public:
Konto(std::string inhaber, double startwert = 0)
: inhaber_(std::move(inhaber)), stand_(startwert) {} // Initialisierungsliste
void einzahlen(double betrag) {
if (betrag <= 0) throw std::invalid_argument("Betrag muss positiv sein");
stand_ += betrag;
}
bool abheben(double betrag) {
if (betrag > stand_) return false;
stand_ -= betrag;
return true;
}
double stand() const { return stand_; } // const: ändert das Objekt nicht
const std::string& inhaber() const { return inhaber_; }
private:
std::string inhaber_;
double stand_;
};
int main() {
Konto k("Mia", 100);
k.einzahlen(50);
std::cout << k.inhaber() << ": " << k.stand() << '\n';
std::cout << k.abheben(30) << ' ' << k.abheben(1000) << ' ' << k.stand() << '\n';
try { k.einzahlen(-5); } catch (const std::exception& e) { std::cout << "Fehler: " << e.what() << '\n'; }
return 0;
}virtual + override für Polymorphie; virtueller Destruktor in Basisklassen= 0) machen Klassen abstrakt (Interface)#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <cmath>
class Form {
public:
virtual ~Form() = default; // virtueller Destruktor: Pflicht bei Vererbung
virtual double flaeche() const = 0; // rein virtuell: abstrakt
virtual std::string name() const { return "Form"; }
void info() const { std::cout << name() << ": " << flaeche() << '\n'; }
};
class Kreis : public Form {
public:
explicit Kreis(double r) : r_(r) {}
double flaeche() const override { return 3.14159 * r_ * r_; }
std::string name() const override { return "Kreis"; }
private:
double r_;
};
class Rechteck : public Form {
public:
Rechteck(double b, double h) : b_(b), h_(h) {}
double flaeche() const override { return b_ * h_; }
std::string name() const override { return "Rechteck"; }
private:
double b_, h_;
};
int main() {
std::vector<std::unique_ptr<Form>> formen;
formen.push_back(std::make_unique<Kreis>(1.0));
formen.push_back(std::make_unique<Rechteck>(3, 4));
for (const auto& f : formen) f->info(); // Polymorphie: die richtige Version wird gewählt
return 0;
}new/delete: std::make_unique, std::make_shared, Containerunique_ptr = ein Besitzer, shared_ptr = geteilter Besitz, weak_ptr beobachtetstd::move überträgt Ressourcen statt sie zu kopieren#include <iostream>
int main() {
int a = 5; // Stack
int* p = new int(7); // Heap (roh!)
std::cout << a << ' ' << *p << '\n';
delete p; // Pflicht, sonst Speicherleck
p = nullptr;
return 0;
}template <typename T> macht Funktionen und Klassen für viele Typen nutzbar, ohne Laufzeitkostenf<int>(...) explizitrequires, std::integral) formulieren Anforderungen und liefern klare Fehlermeldungenif constexpr wählt Code zur Übersetzungszeit; Templates gehören in Header#include <iostream>
#include <string>
template <typename T>
T groesser(T a, T b) { return a > b ? a : b; }
template <typename T, typename U>
auto addiere(T a, U b) { return a + b; }
int main() {
std::cout << groesser(3, 9) << ' ' << groesser(2.5, 1.5) << ' ' << groesser(std::string("a"), std::string("b")) << '\n';
std::cout << addiere(1, 2.5) << ' ' << addiere(std::string("Ha"), "llo") << '\n';
std::cout << groesser<double>(3, 2.5) << '\n'; // Typ explizit angeben
return 0;
}try { } catch (const Typ& e) { }, throw Typ(...); Typen aus <stdexcept>std::runtime_error oder std::exceptionfinally: Destruktoren räumen bei Ausnahmen aufstd::optional, std::variant, std::expected bilden erwartbare Fehler im Typ abassert für Annahmen, static_assert für Prüfungen beim Übersetzen#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
double teile(double a, double b) {
if (b == 0) throw std::invalid_argument("Division durch 0");
return a / b;
}
int main() {
try {
std::cout << teile(10, 4) << '\n';
std::cout << teile(1, 0) << '\n';
} catch (const std::invalid_argument& e) {
std::cout << "Fehler: " << e.what() << '\n';
}
try {
std::vector<int> v{1, 2, 3};
v.at(10);
} catch (const std::out_of_range& e) {
std::cout << "out_of_range\n";
}
try {
std::stoi("abc");
} catch (const std::exception& e) { // allgemeinster Fänger zuletzt
std::cout << "allgemein: " << e.what() << '\n';
}
return 0;
}ifstream (lesen), ofstream (schreiben), stringstream (Strings), alle mit <</>>if (!stream) prüfenstd::getline; Trennzeichen: std::getline(s, teil, ';')std::ios::binary, write, read<iomanip> oder besser std::formatstd::filesystem für Pfade, Ordner, Größen#include <iostream>
#include <fstream>
#include <string>
#include <filesystem>
int main() {
namespace fs = std::filesystem;
const std::string pfad = "notiz.txt";
{
std::ofstream aus(pfad); // schreiben; Datei wird beim Blockende geschlossen (RAII)
if (!aus) { std::cerr << "kann nicht schreiben\n"; return 1; }
aus << "Zeile 1\n" << "Zeile " << 2 << '\n';
}
{
std::ofstream anhang(pfad, std::ios::app); // anhängen
anhang << "Zeile 3\n";
}
std::ifstream ein(pfad);
if (!ein) { std::cerr << "kann nicht lesen\n"; return 1; }
std::string zeile;
int nr = 0;
while (std::getline(ein, zeile)) std::cout << ++nr << ": " << zeile << '\n';
std::cout << fs::exists(pfad) << ' ' << fs::file_size(pfad) << " Bytes\n";
fs::remove(pfad);
std::ifstream fehlt("gibt-es-nicht.txt");
std::cout << (fehlt.is_open() ? "offen" : "nicht gefunden") << '\n';
return 0;
}std::thread/std::jthread starten Threads; join wartetstd::mutex + lock_guard oder std::atomic schützenstd::async/future liefern Ergebnisse (auch Ausnahmen) aus anderen Threadscondition_variable für Warten auf Ereignisse#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
void arbeite(int id) {
std::this_thread::sleep_for(std::chrono::milliseconds(10 * id));
std::cout << "Thread " << id << " fertig\n";
}
int main() {
std::thread t1(arbeite, 1);
std::thread t2(arbeite, 2);
t1.join(); // warten, bis der Thread fertig ist
t2.join();
std::cout << "alle fertig\n";
std::vector<std::jthread> pool; // jthread (C++20): joined automatisch im Destruktor
for (int i = 0; i < 3; i++) pool.emplace_back([i] { (void) i; });
return 0;
}filter, transform, take zu lazy Pipelinesconstexpr/consteval rechnen beim Übersetzen; static_assert prüft dortenum class, nullptr, span, string_view, <=> machen Code sicherer und kürzer<chrono> und <random> ersetzen time() und rand()#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
int main() {
std::vector<int> zahlen{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto ergebnis = zahlen
| std::views::filter([](int x) { return x % 2 == 0; }) // nur gerade
| std::views::transform([](int x) { return x * x; }) // quadrieren
| std::views::take(3); // die ersten drei
for (int x : ergebnis) std::cout << x << ' ';
std::cout << '\n';
for (int i : std::views::iota(1, 6)) std::cout << i << ' '; // 1 bis 5
std::cout << '\n';
for (int x : zahlen | std::views::reverse | std::views::drop(7)) std::cout << x << ' ';
std::cout << '\n';
std::ranges::sort(zahlen, std::greater<>{}); // Algorithmus direkt auf dem Container
std::cout << zahlen.front() << ' ' << std::ranges::count(zahlen, 5) << '\n';
return 0;
}.hpp) deklarieren, .cpp setzen umconst, Standardcontainer, keine Makrosenum class, Smart Pointer, Ranges) machen C++ sicher und lesbarmein-projekt/
├─ CMakeLists.txt
├─ src/
│ ├─ main.cpp
│ ├─ rechner.cpp
│ └─ rechner.hpp
├─ tests/
│ └─ test_rechner.cpp
└─ build/ (erzeugt, nicht ins Repository)class Beispiel {
public:
Beispiel() = default;
explicit Beispiel(int wert) : wert_(wert) {}
virtual ~Beispiel() = default;
int wert() const { return wert_; }
void setze(int w) { wert_ = w; }
virtual std::string name() const { return "Beispiel"; }
bool operator==(const Beispiel&) const = default;
private:
int wert_ = 0;
};
class Abgeleitet final : public Beispiel {
public:
std::string name() const override { return "Abgeleitet"; }
};