Warum C++ selbst Senioren immer noch Angst macht?
\nC++ ist eine Sprache, die Ihnen die volle Kontrolle über den Speicher gibt – aber nur so lange, bis Sie vergessen, ein Semikolon zu setzen, oder versehentlich eine virtuelle Funktion aus einem Konstruktor aufrufen. C++-Vorstellungsgespräche sind berüchtigt für ihre Härte: Sie können sowohl nach Bitfeldern als auch nach dem Aufbau der virtuellen Tabelle und den Feinheiten von std::move gefragt werden. Wenn Sie sich auf ein technisches Interview vorbereiten, ist dieser Artikel Ihre Checkliste. Wir analysieren die tückischsten Themen, die in einem Vorstellungsgespräch fast garantiert auftauchen, und zeigen Ihnen, wie Sie antworten, um nicht durchzufallen.
Auf dem Bildungsportal PythonLib diskutieren wir oft, wie man von Python zu C++ wechselt und umgekehrt. Aber heute geht es um reines C++. Keine Kompromisse.
\n\n1. Virtuelle Funktionen und VTable-Tabellen: Wie man der Falle entgeht
\nFrage: "Was passiert, wenn man eine virtuelle Funktion aus dem Konstruktor der Basisklasse aufruft?" – ein Klassiker. 70 % der Kandidaten antworten falsch. Lassen Sie uns das klären.
\n\nWarum ist das gefährlich?
\nWenn Sie eine virtuelle Funktion im Konstruktor der Basisklasse aufrufen, ist das Objekt der abgeleiteten Klasse noch nicht erstellt. Die virtuelle Tabelle (VTable) zeigt zu diesem Zeitpunkt auf die Methoden der Basisklasse, nicht auf die der abgeleiteten Klasse. Infolgedessen funktioniert der Polymorphismus nicht.
\n\n#include <iostream>\n\nclass Base {\npublic:\n Base() { callFoo(); } // Aufruf im Konstruktor\n virtual void foo() { std::cout << "Base::foo\\n"; }\n void callFoo() { foo(); }\n};\n\nclass Derived : public Base {\npublic:\n Derived() {}\n virtual void foo() override { std::cout << "Derived::foo\\n"; }\n};\n\nint main() {\n Derived d; // Gibt aus: Base::foo, nicht Derived::foo\n return 0;\n}\n\nFazit: Rufen Sie niemals virtuelle Funktionen in Konstruktoren und Destruktoren auf. Wenn der Interviewer fragt, erklären Sie ruhig den Mechanismus der VTable.
\n\n2. Intelligente Zeiger: RAII und Speicherverwaltung
\nOhne dieses Thema kommt kein ernsthaftes C++-Vorstellungsgespräch aus. Fragen zu std::unique_ptr, std::shared_ptr und std::weak_ptr sind die Grundlage. Aber oft wird auch nach zyklischen Referenzen und std::enable_shared_from_this gefragt.
Wie vermeidet man ein Speicherleck mit shared_ptr?
\nStellen Sie sich vor, Sie haben zwei Objekte, die über shared_ptr aufeinander verweisen. Der Referenzzähler wird nie auf Null sinken – der Speicher leckt. Lösung: Einer der Zeiger sollte ein weak_ptr sein.
#include <memory>\n#include <iostream>\n\nstruct Node {\n std::shared_ptr<Node> next;\n std::weak_ptr<Node> prev; // Schwache Referenz, um einen Zyklus zu vermeiden\n ~Node() { std::cout << "Node destroyed\\n"; }\n};\n\nint main() {\n auto a = std::make_shared<Node>();\n auto b = std::make_shared<Node>();\n a->next = b;\n b->prev = a; // weak_ptr erhöht den Zähler nicht\n // Beim Verlassen des Gültigkeitsbereichs werden beide Knoten korrekt gelöscht\n return 0;\n}\n\nErwähnen Sie im Vorstellungsgespräch unbedingt das Konzept RAII (Resource Acquisition Is Initialization) – die Philosophie von C++, die die Lebensdauer einer Ressource an die Lebensdauer eines Objekts bindet.
\n\n3. Move-Semantik und std::move: Magie oder Trick?
\nEine typische Frage: "Was macht std::move?" Die Antwort "es verschiebt das Objekt" ist falsch. std::move wandelt den Typ lediglich in eine Rvalue-Referenz um. Die eigentliche Verschiebung erfolgt im Move-Konstruktor oder Move-Zuweisungsoperator.
Beispiel für das Vorstellungsgespräch
\n#include <string>\n#include <vector>\n#include <iostream>\n\nclass MyString {\n std::string data;\npublic:\n MyString(const std::string& s) : data(s) {}\n // Move-Konstruktor\n MyString(MyString&& other) noexcept : data(std::move(other.data)) {\n std::cout << "Move constructor c