Junior C++ voorbereiding

Interviewvragen voor junior C++-backenddevelopers

15 geselecteerde Junior C++ interviewvragen voor backenddevelopers die de fundamenten van de taal en basaal eigenaarschap helder moeten kunnen uitleggen.

Start een junior C++-interview met AIGeen creditcard nodig. 1 gratis sessie beschikbaar.
Oefen technische interviews in het EngelsEen modus waarin anderstaligen kunnen oefenen met het afleggen van technische interviews.

Resource Management

1Leg uit wat RAII (Resource Acquisition Is Initialization) is en hoe het resourcebeheer veilig ten opzichte van uitzonderingen maakt in C++ backend-services.

RAII houdt in dat een C++-object eigenaar is van een resource en deze weer vrijgeeft in zijn destructor. Omdat lokale objecten automatisch worden vernietigd wanneer hun levensduur of bereik eindigt, inclusief tijdens het afwikkelen van de stack bij een uitzondering, zorgt RAII voor deterministisch opruimen en maakt het foutpaden veilig ten opzichte van uitzonderingen. In backend-services is dit niet alleen van toepassing op geheugen, maar ook op bestandsdescriptoren, sockets, mutex-locks, database-handles, transacties en andere besturingssysteem- of applicatieresources.

Probeer deze vraag te beantwoorden met een AI-begeleider

Geheugenbeheer

2Vergelijk `std::unique_ptr` en `std::shared_ptr` en beschrijf wanneer elk geschikt is in backend-API's.

`std::unique_ptr` vertegenwoordigt exclusief eigendom: het is goedkoop, verplaatsbaar maar niet kopieerbaar, en is geschikt voor een enkele eigenaar of voor API's die eigendom overdragen. `std::shared_ptr` vertegenwoordigt gedeeld eigendom: het is kopieerbaar en houdt een object in leven door middel van referentietelling totdat de laatste sterke eigenaar het vrijgeeft. Gebruik in backend-API's `unique_ptr` wanneer eigendom wordt overgedragen, `shared_ptr` alleen wanneer meerdere onafhankelijke eigenaren de levensduur moeten verlengen, en referenties of ruwe pointers voor toegang zonder eigenaarschap. Het gebruik van `std::make_shared` heeft doorgaans de voorkeur bij het maken van `shared_ptr`-objecten omdat het efficiënt is en veilig ten opzichte van uitzonderingen.

Probeer deze vraag te beantwoorden met een AI-begeleider

Typesysteem

3Wat is move-semantiek en hoe implementeer je een correcte move-constructor en move-toewijzingsoperator?

Move-semantiek stelt C++ in staat om resources over te dragen van tijdelijke of anderszins vervangbare objecten in plaats van ze te kopiëren. Het maakt gebruik van rvalue-referenties zoals `T&&` en `std::move`, wat een cast is die het mogelijk maakt om move-overloads te selecteren; `std::move` zelf verplaatst niets. Een correcte move-constructor initialiseert een nieuw object door de resource van het bronobject over te nemen, en laat de bron achter in een toestand die geldig is en veilig vernietigd of toegewezen kan worden. Een correcte move-toewijzingsoperator verplaatst resources naar een bestaand object, handelt zelftoewijzing correct af, geeft de huidige resource van het doel vrij of hergebruikt deze, neemt de resource van de bron over en laat de bron in een veilige staat achter. Move-operaties moeten vaak `noexcept` zijn, zodat standaardcontainers deze kunnen gebruiken tijdens herallocaties, met behoud van de garanties bij uitzonderingen.

Probeer deze vraag te beantwoorden met een AI-begeleider

4Beschrijf const-correctness in C++ API's en hoe je effectief const-memberfuncties ontwerpt.

Const-correctness betekent dat je via het typesysteem uitdrukt welke bewerkingen de waarneembare of logische toestand van een object niet wijzigen. Een `const`-memberfunctie heeft een const-gekwalificeerd `this`-object, dus het kan geen niet-veranderlijke datamembers wijzigen of niet-`const` memberfuncties op hetzelfde object aanroepen. Een goed API-ontwerp markeert alleen-lezen query's als `const`, retourneert waarden of `const`-referenties/pointers wanneer dat gepast is, en vermijdt het blootstellen van een veranderlijke interne toestand vanuit `const`-functies. Het sleutelwoord `mutable` moet gereserveerd worden voor implementatiedetails die de logische toestand niet veranderen, zoals caches, lazy waarden, metrieken of mutexen. `const` is een API-contract over mutatie, geen automatische garantie voor threadveiligheid; garanties voor gelijktijdigheid vereisen een afzonderlijke implementatie en documentatie.

Probeer deze vraag te beantwoorden met een AI-begeleider

5Wat is `std::byte` en hoe moeten onbewerkte binaire buffers veilig worden gerepresenteerd in C++?

`std::byte` is een apart type om ruwe binaire data te representeren als bytes, in plaats van als tekens of rekenkundige gehele getallen. Het verbetert de typeveiligheid doordat bytebuffers niet per ongeluk als tekst of numerieke waarden worden behandeld, terwijl bitgewijze operaties nog wel worden ondersteund. Ruwe binaire buffers moeten doorgaans worden opgeslagen met byte-georiënteerde opslag, zoals `std::vector<std::byte>` of `std::array<std::byte, N>`, en worden doorgegeven via API's zonder overdracht van eigenaarschap als `std::span<std::byte>` of `std::span<const std::byte>`. Serialisatiecode moet waarden expliciet coderen en decoderen, in plaats van te vertrouwen op willekeurige geheugenlay-outs van objecten.

Probeer deze vraag te beantwoorden met een AI-begeleider

Levensduur van objecten

6Beschrijf de Rule of Zero, Rule of Three en Rule of Five, en geef aan wanneer elke regel van toepassing is.

Rule of Zero: geef de voorkeur aan klassen die geen aangepaste destructor-, kopieer- of verplaatsingsoperaties (move) declareren; laat RAII (Resource Acquisition Is Initialization)-leden zoals `std::string`, `std::vector`, `std::unique_ptr`, bestands- of socket-wrappers, enzovoort, de bronnen beheren. Rule of Three: als een klasse handmatig een bron beheert en een aangepaste destructor, kopieerconstructor of kopieertoewijzingsoperator (copy assignment) nodig heeft, zijn doorgaans alle drie nodig om het juiste kopieer- en eigenaarschapgedrag te definiëren. Rule of Five: in C++11 en later moeten dergelijke typen ook een move-constructor en move-toewijzingsoperator overwegen. Gebruik de Rule of Zero voor de meeste typen in een applicatie; gebruik de Rule of Three/Five wanneer het type direct eigenaar is van een bron of niet-triviale semantiek voor eigenaarschap of levensduur heeft.

Probeer deze vraag te beantwoorden met een AI-begeleider

Foutafhandeling

7Leg uit hoe de afhandeling van uitzonderingen, stack unwinding, de interactie met destructors en uitzonderingsgrenzen (exception boundaries) van services in C++ werken.

Uitzonderingen (exceptions) in C++ dragen de besturing over van een `throw`-expressie naar het dichtstbijzijnde overeenkomende `catch`-blok. Tijdens de propagatie vernietigt de *stack unwinding* volledig geconstrueerde automatische objecten in omgekeerde volgorde, zodat de RAII-opruiming (Resource Acquisition Is Initialization) automatisch wordt uitgevoerd. Destructors mogen in de regel geen uitzonderingen gooien; als een uitzondering ontsnapt uit een `noexcept`-destructor, of als er een andere uitzondering ontsnapt tijdens actieve unwinding, roept het programma `std::terminate` aan. Uitzonderingen moeten doorgaans via een referentie worden gevangen, meestal `const&`, om *object slicing* en onnodige kopieën te voorkomen. Backend-services moeten grenzen definiëren voor uitzonderingen. Dit gebeurt vaak bij de afhandelaars van aanvragen, instappunten van *worker threads*, callbacks van een RPC/HTTP-framework en in `main`. Op deze locaties worden uitzonderingen gelogd, geconverteerd naar foutmeldingen of HTTP-statuscodes, en wordt voorkomen dat ze weglekken naar ongepaste contexten zoals C-API's, destructors, threads of `noexcept`-functies.

Probeer deze vraag te beantwoorden met een AI-begeleider

8Wat gebeurt er als een destructor een uitzondering opwerpt, en hoe zouden backend-typen mislukte opschoonacties moeten rapporteren?

Destructors zijn in normale gevallen impliciet `noexcept(true)`, dus als een uitzondering uit een dergelijke destructor ontsnapt, wordt `std::terminate` aangeroepen. Zelfs als een destructor expliciet als `noexcept(false)` is gedeclareerd, is het opwerpen van een uitzondering tijdens stack-unwinding gevaarlijk; een tweede ontsnappende uitzondering, terwijl er al een andere uitzondering actief is, beëindigt namelijk ook het programma. Daarom moeten destructors opschoonacties naar best vermogen uitvoeren en geen uitzonderingen laten ontsnappen. Backend-typen moeten mislukte opschoonacties rapporteren via expliciete bewerkingen zoals `close()`, `flush()`, `commit()`, `stop()` of `shutdown()`, die een fout of `expected` retourneren, of een uitzondering opwerpen voorafgaand aan de destructie. De destructor kan loggen, metrieken uitzenden, fouten onderdrukken of veilige alternatieve opschoonacties uitvoeren, maar mag niet het primaire foutrapportagekanaal zijn voor fouten die om een actie vragen.

Probeer deze vraag te beantwoorden met een AI-begeleider

Standaardbibliotheek

9Hoe beheert `std::vector` capaciteit, groei, herallocatie en de stabiliteit van iterators?

`std::vector` slaat elementen aaneengesloten op en houdt zowel `size` als `capacity` bij. `size` is het aantal geconstrueerde elementen; `capacity` is de hoeveelheid toegewezen opslagruimte voor elementen die beschikbaar is voordat een nieuwe allocatie nodig is. Wanneer het toevoegen van elementen de capaciteit zou overschrijden, wijst de vector een groter blok toe, meestal met behulp van een implementatie-afhankelijke geometrische groeistrategie. Hierbij verplaatst of kopieert de vector bestaande elementen, vernietigt hij de oude elementen en geeft hij de oude opslagruimte vrij. `reserve(n)` verhoogt de capaciteit zonder de grootte te veranderen, terwijl `resize(n)` de grootte verandert door elementen te construeren of te vernietigen. Herallocatie maakt alle iterators, referenties en pointers naar elementen ongeldig; zelfs zonder herallocatie kunnen bewerkingen zoals `insert` en `erase` posities op of na het wijzigingspunt ongeldig maken.

Probeer deze vraag te beantwoorden met een AI-begeleider

10Welke invalidatieregels moet je kennen voor aaneengesloten en op nodes gebaseerde standaardcontainers?

Invalidatieregels hangen af van de container en de operatie. Aaneengesloten containers zoals vector en string hebben een kwetsbare stabiliteit qua iterators en referenties: groei kan leiden tot herallocatie en alle iterators, referenties en pointers ongeldig maken. Ook kunnen insert- en erase-operaties elementen verschuiven, waardoor posities op of na de wijziging ongeldig worden, zelfs zonder herallocatie. Geordende, op nodes gebaseerde containers zoals list, map, set en hun multi-varianten houden iterators en referenties naar bestaande, niet-gewiste elementen over het algemeen stabiel tijdens invoegoperaties; het wissen van een element invalideert uitsluitend de iterator of referentie naar dat specifieke gewiste element. Ongeordende containers slaan elementen ook op in nodes, waardoor referenties en pointers naar elementen doorgaans stabiel blijven tijdens een rehash, maar een rehash maakt iterators wel ongeldig. Voor deque gelden speciale regels voor gesegmenteerde opslag. In de praktijk is het raadzaam om de specifieke container en operatie te controleren voordat je iterators of referenties over containerwijzigingen heen bewaart.

Probeer deze vraag te beantwoorden met een AI-begeleider

11Vergelijk `std::map`, `std::unordered_map` en containers in de stijl van een flat-map voor zoektabellen in een backend.

`std::map` is een geordende, doorgaans op bomen gebaseerde associatieve container met logaritmische complexiteit voor opzoeken, invoegen en verwijderen; het is nuttig wanneer gesorteerde iteratie, bereikquery's of indelingsgaranties belangrijk zijn. `std::unordered_map` is gebaseerd op hashtabellen met een gemiddelde constante tijd voor bewerkingen op exacte sleutels en zonder sleutelordening; het is vaak een goede standaardkeuze voor grote, veranderlijke zoektabellen als de hashfunctie goed is. Een container in flat-map-stijl slaat gesorteerde sleutel-waardeparen aaneengesloten op, wat een goede cachelokaliteit en snelle iteratie/opzoeken met binaire zoekopdrachten oplevert, maar invoegen en verwijderen in het midden kosten lineaire tijd. Kies voor zoektabellen in een backend op basis van de vraag of de werklast ordening of bereiken vereist, voornamelijk exact opzoeken nodig heeft, vaak muteert, of er behoefte is aan voorspelbare latentie, geheugenoverhead en specifiek cachegedrag.

Probeer deze vraag te beantwoorden met een AI-begeleider

12Leg `std::optional` uit en typische backend-toepassingen voor het weergeven van ontbrekende waarden.

`std::optional<T>` vertegenwoordigt ofwel een aanwezige `T`-waarde ofwel geen waarde. De lege status wordt weergegeven door `std::nullopt`; code kan `has_value()` controleren of de optional in een booleaanse context gebruiken, de waarde benaderen met `*` of `value()`, en een standaardwaarde opgeven met `value_or()`. In backend-code is het nuttig voor databasevelden die null mogen zijn, optionele request- of configuratievelden, ontbrekende waarden in caches of repositories waarbij afwezigheid wordt verwacht, en domeinstatussen waarbij een sentinelwaarde zoals `-1` of een lege string dubbelzinnig zou zijn. Het modelleert de afwezigheid van een waarde, geen polymorfisme of uitgebreide foutinformatie.

Probeer deze vraag te beantwoorden met een AI-begeleider

13Wat zijn std::string_view en std::span, en welke gevaren met betrekking tot de levensduur introduceren views zonder eigenaarschap?

std::string_view is een view zonder eigenaarschap van een aaneengesloten reeks tekens; std::span<T> is een view zonder eigenaarschap van een aaneengesloten reeks van T. Ze zijn nuttig voor zero-copy-parameters en buffer-API's, omdat ze een pointer en een lengte bevatten zonder geheugen toe te wijzen of als eigendom te beheren. Het belangrijkste gevaar is de levensduur: de gerefereerde opslag moet langer blijven bestaan dan de view en mag niet ongeldig worden gemaakt zolang de view in gebruik is. Het retourneren of opslaan van een view naar een tijdelijk object, een lokaal object, een vernietigd object of een geheralloceerde container kan leiden tot een dangling view.

Probeer deze vraag te beantwoorden met een AI-begeleider

Concurrency

14Waarin verschillen std::mutex, std::shared_mutex en std::recursive_mutex van elkaar, en wanneer zou je voor elk daarvan kiezen?

std::mutex biedt exclusieve vergrendeling: slechts één thread kan deze vasthouden, waardoor het de standaardkeuze is voor het beschermen van gedeelde, veranderlijke status. std::shared_mutex ondersteunt gedeelde leeslocks en exclusieve schrijflocks: meerdere lezers kunnen de lock gelijktijdig vasthouden, maar schrijvers hebben exclusieve toegang nodig. Kies deze voor data die voornamelijk wordt gelezen, wanneer de gelijktijdigheid van lezers opweegt tegen de overhead en wanneer het acceptabel is (of wordt afgevangen) dat schrijvers mogelijk niet aan bod komen. std::recursive_mutex is een exclusieve mutex die door dezelfde thread meerdere keren kan worden vergrendeld en even vaak moet worden ontgrendeld; gebruik deze zelden, voornamelijk voor legacy of re-entrante code, omdat het een slecht lock-ontwerp kan verbergen.

Probeer deze vraag te beantwoorden met een AI-begeleider

Taalfunctionaliteiten

15Leg de capture-modi van lambda's uit en hoe deze captures omgaan met de levensduur van objecten in callbacks.

Een lambda kan variabelen vastleggen op waarde (`[x]` of `[=]`), op referentie (`[&x]` of `[&]`), `this` vastleggen, of init-capture gebruiken zoals `[p = std::move(ptr)]`. Vastleggen op waarde kopieert het object naar de closure wanneer de lambda wordt aangemaakt; vastleggen op referentie slaat referenties op, waardoor de oorspronkelijke objecten langer moeten blijven bestaan dan alle lambda-aanroepen. In callbacks, opgeslagen lambda's of asynchroon werk zijn referentie-captures en `this`-captures gevaarlijk, omdat lokale variabelen of het object al vernietigd kunnen zijn vóór de aanroep. Geef de voorkeur aan vastleggen op waarde voor benodigde gegevens, move/init-capture voor de overdracht van eigenaarschap, of expliciete `shared_ptr`/`weak_ptr`-patronen wanneer de levensduur van een object moet worden verlengd of gecontroleerd.

Probeer deze vraag te beantwoorden met een AI-begeleider