Junior-C++-Vorbereitung

Interviewfragen für Junior-C++-Backend-Entwickler

15 ausgewählte Fragen für Junior-C++-Entwickler, die Sprachgrundlagen und einfache Ressourcenverwaltung klar erklären müssen.

Junior-C++-KI-Interview startenKeine Kreditkarte erforderlich. 1 kostenlose Session verfügbar.
Technische Interviews auf Englisch übenEin Modus, in dem Nicht-Muttersprachler technische Interviews üben können.

Resource Management

1Erklären Sie RAII und wie es ausnahmesichere Ressourcenverwaltung in C++-Backend-Services prägt.

RAII (Resource Acquisition Is Initialization) bedeutet, dass ein C++-Objekt eine Ressource besitzt und sie in seinem Destruktor freigibt. Da lokale Objekte automatisch zerstört werden, wenn ihre Lebensdauer bzw. ihr Scope endet, auch während des Stack-Unwindings bei Exceptions, ermöglicht RAII deterministisches Aufräumen und macht Fehlerpfade ausnahmesicher. In Backend-Services gilt dies nicht nur für Speicher, sondern auch für Dateideskriptoren, Sockets, Mutex-Locks, Datenbank-Handles, Transaktionen und andere Betriebssystem- oder Anwendungsressourcen.

Diese Frage mit einem KI-Coach beantworten

Speicherverwaltung

2Vergleichen Sie std::unique_ptr und std::shared_ptr und beschreiben Sie, wann welcher in Backend-APIs geeignet ist.

std::unique_ptr repräsentiert exklusiven Besitz: er ist leichtgewichtig, movable, aber nicht kopierbar, und eignet sich für einen einzelnen Besitzer oder für APIs, die Besitz übertragen. std::shared_ptr repräsentiert geteilten Besitz: er ist kopierbar und hält ein Objekt über Reference Counting am Leben, bis der letzte starke Besitzer es freigibt. In Backend-APIs verwendet man unique_ptr, wenn Besitz übertragen wird, shared_ptr nur dann, wenn mehrere unabhängige Besitzer die Lebensdauer verlängern müssen, und Referenzen oder rohe Pointer für nicht besitzenden Zugriff. std::make_shared wird beim Erzeugen von shared_ptr-Objekten häufig bevorzugt, weil es effizient und ausnahmesicher ist.

Diese Frage mit einem KI-Coach beantworten

Typsystem

3Was sind Move Semantics, und wie implementiert man korrekte Move-Konstruktion und Move-Zuweisung?

Move Semantics erlauben C++, Ressourcen aus temporären oder anderweitig entbehrlichen Objekten zu übertragen, statt sie zu kopieren. Sie verwenden Rvalue-Referenzen wie T&& und std::move; std::move ist ein Cast, der ermöglicht, dass Move-Overloads ausgewählt werden; std::move selbst bewegt nichts. Ein korrekter Move-Konstruktor initialisiert ein neues Objekt, indem er die Ressource des Quellobjekts übernimmt und die Quelle gültig, zerstörbar und zuweisbar zurücklässt. Ein korrekter Move-Zuweisungsoperator überträgt in ein bestehendes Objekt, behandelt oder toleriert Selbstzuweisung, gibt die aktuelle Ressource des Ziels frei oder verwendet sie wieder, übernimmt die Ressource der Quelle und lässt die Quelle sicher zurück. Move-Operationen sollten oft noexcept sein, damit Standardcontainer sie während Reallokationen verwenden können und dabei Exception-Garantien erhalten bleiben.

Diese Frage mit einem KI-Coach beantworten

4Beschreiben Sie const-Korrektheit in C++-APIs und wie man const-Memberfunktionen wirksam entwirft.

Const-Korrektheit bedeutet, über das Typsystem auszudrücken, welche Operationen den beobachtbaren oder logischen Zustand eines Objekts nicht verändern. Eine const-Memberfunktion hat ein const-qualifiziertes this-Objekt, daher kann sie keine nicht-mutable-Datenmember verändern und keine nicht-const-Memberfunktionen auf demselben Objekt aufrufen. Gutes API-Design markiert schreibgeschützte Abfragen als const, gibt Werte oder const-Referenzen/-Zeiger zurück, wo es angemessen ist, und vermeidet, veränderbaren internen Zustand aus const-Funktionen heraus offenzulegen. mutable sollte für Implementierungsdetails reserviert sein, die den logischen Zustand nicht ändern, etwa Caches, lazy Werte, Metriken oder Mutexe. const ist ein API-Vertrag über Mutation, keine automatische Garantie für Thread-Sicherheit; Nebenläufigkeitsgarantien benötigen eigene Implementierung und Dokumentation.

Diese Frage mit einem KI-Coach beantworten

5Was ist std::byte und wie sollten rohe Binärpuffer in C++ sicher repräsentiert werden?

std::byte ist ein eigener Typ zur Darstellung roher Binärdaten als Bytes, nicht als Zeichen oder arithmetische Ganzzahlen. Er verbessert die Typsicherheit, weil Byte-Puffer nicht versehentlich als Text oder numerische Werte behandelt werden, und unterstützt dennoch bitweise Operationen. Rohe Binärpuffer sollten in der Regel mit byte-orientiertem Speicher wie std::vector<std::byte> oder std::array<std::byte, N> dargestellt und über nicht-besitzende APIs als std::span<std::byte> oder std::span<const std::byte> übergeben werden. Serialisierungscode sollte Werte explizit enkodieren und dekodieren, statt sich auf willkürliche Objektlayouts zu verlassen.

Diese Frage mit einem KI-Coach beantworten

Objektlebensdauer

6Beschreiben Sie die Rule of Zero, die Rule of Three und die Rule of Five und wann sie jeweils gelten.

Rule of Zero: Bevorzugen Sie Klassen, die keine eigenen Destruktor-/Kopier-/Verschiebeoperationen deklarieren; lassen Sie RAII-Member wie std::string, std::vector, std::unique_ptr, Datei-/Socket-Wrapper usw. Ressourcen verwalten. Rule of Three: Wenn eine Klasse eine Ressource manuell verwaltet und einen eigenen Destruktor, Kopierkonstruktor oder Kopierzuweisungsoperator benötigt, benötigt sie üblicherweise alle drei, um korrektes Kopier- und Besitzverhalten zu definieren. Rule of Five: In C++11 und später sollten solche Typen zusätzlich Verschiebekonstruktor und Verschiebezuweisungsoperator berücksichtigen. Verwenden Sie die Rule of Zero für die meisten Anwendungstypen; verwenden Sie die Rule of Three/Five, wenn der Typ direkt eine Ressource besitzt oder nichttriviale Besitz-/Lebenszeitsemantik hat.

Diese Frage mit einem KI-Coach beantworten

Fehlerbehandlung

7Erklären Sie Exception Handling in C++, Stack Unwinding, das Zusammenspiel mit Destruktoren und Exception-Grenzen in Services.

C++-Exceptions übertragen die Kontrolle von einem `throw`-Ausdruck zum nächstpassenden `catch`. Während der Propagation zerstört Stack Unwinding vollständig konstruierte automatische Objekte in umgekehrter Reihenfolge, sodass RAII-Aufräumen automatisch abläuft. Destruktoren sollten in der Regel nicht werfen; entweicht eine Exception einem `noexcept`-Destruktor oder entweicht während eines aktiven Unwindings eine weitere Exception, ruft das Programm `std::terminate` auf. Exceptions sollten üblicherweise per Referenz gefangen werden, häufig als `const&`, um Slicing und unnötige Kopien zu vermeiden. Backend-Services sollten Exception-Grenzen definieren, etwa Request-Handler, Einstiegspunkte von Worker-Threads, RPC/HTTP-Framework-Callbacks und `main`, an denen Exceptions geloggt, in Fehlerantworten oder Statuscodes umgewandelt und daran gehindert werden, in ungeeignete Kontexte wie C-APIs, Destruktoren, Threads oder `noexcept`-Funktionen zu entweichen.

Diese Frage mit einem KI-Coach beantworten

8Was passiert, wenn ein Destruktor wirft, und wie sollten Backend-Typen Fehler beim Aufräumen melden?

Destruktoren sind in normalen Fällen implizit `noexcept(true)`, sodass bei einer entweichenden Exception aus einem solchen Destruktor `std::terminate` aufgerufen wird. Selbst wenn ein Destruktor explizit als `noexcept(false)` deklariert ist, ist Werfen während des Stack Unwindings gefährlich, weil eine zweite entweichende Exception bei bereits aktiver Exception das Programm ebenfalls beendet. Deshalb sollten Destruktoren bestmöglich aufräumen und Exceptions nicht entweichen lassen. Backend-Typen sollten Fehler beim Aufräumen über explizite Operationen wie `close()`, `flush()`, `commit()`, `stop()` oder `shutdown()` melden, die einen Fehler/`expected` zurückgeben oder vor der Zerstörung werfen. Der Destruktor kann loggen, Metriken emittieren, Fehler unterdrücken oder sicheres Fallback-Aufräumen ausführen, sollte aber nicht der primäre Fehlerkanal für handlungsrelevante Fehler sein.

Diese Frage mit einem KI-Coach beantworten

Standardbibliothek

9Wie verwaltet std::vector Kapazität, Wachstum, Reallokation und Iterator-Stabilität?

std::vector speichert Elemente zusammenhängend und verfolgt sowohl size als auch capacity. size ist die Anzahl konstruierter Elemente; capacity ist der verfügbare allokierte Elementspeicher, bevor eine weitere Allokation nötig ist. Wenn das Hinzufügen von Elementen die Kapazität überschreiten würde, allokiert vector einen größeren Block, typischerweise mit einer implementationsdefinierten geometrischen Wachstumsstrategie, movet oder kopiert bestehende Elemente, zerstört die alten und gibt den alten Speicher frei. reserve(n) erhöht die Kapazität, ohne size zu ändern, während resize(n) size ändert, indem Elemente konstruiert oder zerstört werden. Reallokation invalidiert alle Iteratoren, Referenzen und Zeiger auf Elemente; selbst ohne Reallokation können Operationen wie insert und erase Positionen an oder nach dem Änderungspunkt invalidieren.

Diese Frage mit einem KI-Coach beantworten

10Welche Invalidierungsregeln sollte man für zusammenhängende und knotenbasierte Standardcontainer kennen?

Invalidierungsregeln hängen vom Container und der Operation ab. Zusammenhängende Container wie vector und string haben fragile Iterator-/Referenzstabilität: Wachstum kann reallokieren und alle Iteratoren, Referenzen und Zeiger invalidieren, und insert/erase kann Elemente verschieben und Positionen an oder nach der Änderung auch ohne Reallokation invalidieren. Knotenbasierte geordnete Container wie list, map, set und ihre multi-Varianten halten Iteratoren und Referenzen auf bestehende nicht gelöschte Elemente bei insert im Allgemeinen stabil; das Löschen eines Elements invalidiert den Iterator/die Referenz auf dieses gelöschte Element. Unordered-Container speichern Elemente ebenfalls in Knoten, daher bleiben Referenzen und Zeiger auf Elemente über Rehash hinweg im Allgemeinen stabil, aber Rehash invalidiert Iteratoren. deque hat besondere Regeln für segmentierten Speicher. In der Praxis sollte man den konkreten Container und die Operation prüfen, bevor man Iteratoren oder Referenzen über Modifikationen hinweg speichert.

Diese Frage mit einem KI-Coach beantworten

11Vergleichen Sie std::map, std::unordered_map und Container im Flat-Map-Stil für Lookup-Tabellen im Backend.

std::map ist ein geordneter, üblicherweise baumbasierter assoziativer Container mit logarithmischem Lookup/Einfügen/Löschen; er ist nützlich, wenn sortierte Iteration, Bereichsabfragen oder Ordnungsgarantien wichtig sind. std::unordered_map basiert auf einer Hashtabelle mit im Durchschnitt konstanter Zeit für exakte Schlüsseloperationen und ohne Schlüsselordnung; er ist oft eine gute Standardwahl für große, veränderliche Lookup-Tabellen, wenn das Hashing gut ist. Ein Container im Flat-Map-Stil speichert sortierte Schlüssel/Wert-Paare zusammenhängend, bietet gute Cache-Lokalität und schnelle Iteration/binäre Suche, aber Einfügen und Löschen in der Mitte sind linear. Für Backend-Lookup-Tabellen wählen Sie je nachdem, ob die Last Ordnung/Bereiche, überwiegend exakte Lookups, häufige Mutation, vorhersehbare Latenz, Speicher-Overhead und Cache-Verhalten benötigt.

Diese Frage mit einem KI-Coach beantworten

12Erklären Sie std::optional und typische Backend-Anwendungsfälle zur Darstellung fehlender Werte.

std::optional<T> repräsentiert entweder einen enthaltenen T-Wert oder keinen Wert. Der leere Zustand wird durch std::nullopt dargestellt; Code kann has_value() prüfen oder das optional im booleschen Kontext nutzen, den Wert mit * oder value() zugreifen und mit value_or() einen Default liefern. Im Backend ist es nützlich für nullable Datenbankfelder, optionale Request-/Konfigurationsfelder, Cache- oder Repository-Misses, bei denen Abwesenheit erwartet wird, und Domänenzustände, in denen ein Sentinel wie -1 oder ein leerer String mehrdeutig wäre. Es modelliert die Abwesenheit eines Werts, nicht Polymorphie oder reichhaltige Fehlerinformationen.

Diese Frage mit einem KI-Coach beantworten

13Was sind std::string_view und std::span, und welche Lebenszeitrisiken bringen nicht-besitzende Views mit sich?

std::string_view ist ein nicht-besitzender View auf eine zusammenhängende Zeichenfolge; std::span<T> ist ein nicht-besitzender View auf eine zusammenhängende Sequenz von T. Sie eignen sich für Zero-Copy-Parameter und Puffer-APIs, weil sie einen Zeiger und eine Länge tragen, ohne Speicher zu allozieren oder zu besitzen. Das Hauptrisiko ist die Lebenszeit: Der referenzierte Speicher muss den View überleben und darf nicht ungültig werden, solange der View verwendet wird. Das Zurückgeben oder Speichern eines Views auf ein Temporäres, ein lokales Objekt, ein zerstörtes Objekt oder einen reallokierten Container kann einen dangling View hinterlassen.

Diese Frage mit einem KI-Coach beantworten

Nebenläufigkeit

14Worin unterscheiden sich std::mutex, std::shared_mutex und std::recursive_mutex, und wann würde man jeweils welchen wählen?

std::mutex bietet exklusives Locking: Nur ein Thread kann ihn halten, daher ist er die Standardwahl zum Schutz gemeinsamen mutablen Zustands. std::shared_mutex unterstützt geteilte Reader-Locks und exklusive Writer-Locks: Viele Reader dürfen den Lock gleichzeitig halten, Writer benötigen jedoch exklusiven Zugriff. Man wählt ihn für read-mostly-Daten, wenn Reader-Nebenläufigkeit den Overhead wert ist und Writer-Fairness/Starvation akzeptabel oder gehandhabt ist. std::recursive_mutex ist ein exklusiver Mutex, den derselbe Thread mehrfach locken darf und genauso oft unlocken muss; man nutzt ihn selten, vor allem für Legacy- oder reentranten Code, weil er schlechtes Locking-Design verbergen kann.

Diese Frage mit einem KI-Coach beantworten

Sprachfeatures

15Erklären Sie die Capture-Modi von Lambdas und wie Captures mit Objektlebenszeiten in Callbacks zusammenwirken.

Ein Lambda kann Variablen per Wert (`[x]` oder `[=]`), per Referenz (`[&x]` oder `[&]`) capturen, `this` capturen oder Init-Capture wie `[p = std::move(ptr)]` verwenden. Value-Captures kopieren das gecapturete Objekt beim Erzeugen des Lambdas in die Closure; Reference-Captures speichern Referenzen, sodass die Originalobjekte alle Lambda-Aufrufe überleben müssen. In Callbacks, gespeicherten Lambdas oder asynchroner Arbeit sind Reference-Captures und `this`-Captures gefährlich, weil lokale Variablen oder das Objekt vor dem Aufruf zerstört sein können. Bevorzugen Sie Value-Captures für benötigte Daten, Move-/Init-Capture für Ownership oder bewusste `shared_ptr`/`weak_ptr`-Muster, wenn die Objektlebenszeit verlängert oder geprüft werden muss.

Diese Frage mit einem KI-Coach beantworten