Logo PointerLab

Entretien C++ : 12 questions pièges avec réponses (2026)

Entretien C++ : 12 questions pièges avec réponses (2026)

Amine Abidi - Lead Software Engineer C++/Qt - Co-fondateur PointerLab

Publié par Amine Abidi - Lead Software Engineer C++/Qt - Co-fondateur PointerLab

Entretien C++ : 12 questions pièges (avec réponses testées)

« Quelle est la différence entre unique_ptr et shared_ptr ? » Tous les candidats C++ répondent à celle-là. C'est la question suivante qui trie : « Et quand votre shared_ptr ne libère-t-il jamais rien ? »

Ce guide réunit 12 questions d'entretien C++ qui piègent régulièrement même des développeurs expérimentés : durée de vie des objets, sémantique de déplacement, comportement indéfini, concurrence, embarqué et Qt. Pour chacune : le piège, la réponse en quelques phrases, un code réellement compilé et exécuté (avec AddressSanitizer ou ThreadSanitizer quand c'était pertinent) et ce que l'intervieweur cherche à évaluer.


L'essentiel en chiffres

Chiffre Ce qu'il mesure
12 questions, réparties en 6 thèmes
11 sur 12 questions illustrées par un code compilé et exécuté (la 12ᵉ s'appuie sur la documentation Qt 6)
5 moyens de preuve utilisés : warnings de compilation, AddressSanitizer, LeakSanitizer, ThreadSanitizer, lecture de l'assembleur
2 compilateurs testés : GCC 13.3 et Clang 18.1
C++11 → C++23 standards couverts par les exemples

Pourquoi ces questions comptent en 2026

Trois chiffres de contexte expliquent la place des questions de mémoire et de comportement indéfini dans ce guide :

  • 27 % des 24 534 développeurs de 194 pays interrogés par JetBrains en 2025 déclarent avoir utilisé C++ au cours des 12 derniers mois (synthèse DevNewsletter).
  • Environ 70 % des vulnérabilités à grande échelle sont liées à des problèmes de mémoire, un ordre de grandeur souvent attribué à des études de Microsoft et de Google (même synthèse).
  • C++26 a été finalisé en mars 2026 : la bibliothèque standard « durcie » (proposition P3471) y entre, mais les profiles de sécurité sont renvoyés à C++29 (analyse Whole Tomato). Nous détaillons ce qui change dans notre article C++26 : réflexion, contrats et sécurité mémoire.

Comment répondre : la méthode en trois temps

Une réponse solide tient presque toujours en trois temps :

  1. La règle : ce que dit le langage, en une phrase.
  2. Le cas où elle casse : l'exemple qui piège.
  3. La détection ou la parade : l'outil, l'option de compilation ou le type qui évite le problème.

Chaque question ci-dessous suit ce plan. Les exemples se compilent avec g++ -std=c++23 -Wall -Wextra -pedantic.


Les 12 questions en un coup d'œil

N° Question Thème Niveau indicatif Repéré par (testé)
1 Quand un shared_ptr ne libère-t-il jamais rien ? Durée de vie Confirmé LeakSanitizer
2 Une référence sur un élément de vector survit-elle à un push_back ? Durée de vie Junior AddressSanitizer
3 string_view est-il toujours plus sûr que const string& ? Durée de vie Confirmé AddressSanitizer
4 std::move déplace-t-il quelque chose ? Sémantique Confirmé Sortie du programme
5 vector<int> a(5, 1) et vector<int> b{5, 1} : combien d'éléments ? Langage Junior Sortie du programme
6 Pourquoi un destructeur de base doit-il être virtual ? Langage Junior Warning de -Wall
7 Que se passe-t-il si un constructeur lance une exception ? Exceptions, RAII Confirmé Sortie du programme
8 Le compilateur a supprimé votre test : en a-t-il le droit ? Comportement indéfini Senior Assembleur, deux compilateurs
9 volatile rend-il un accès thread-safe ? Concurrence, embarqué Confirmé ThreadSanitizer
10 Deux mutex verrouillés dans deux ordres différents ? Concurrence Confirmé Observation du blocage
11 Comment gérer les erreurs sans exceptions ? C++ moderne, embarqué Confirmé Compilation -fno-exceptions
12 Dans quel thread s'exécute un slot Qt ? Qt Confirmé Documentation Qt 6

Mémoire et durée de vie

1. Quand un shared_ptr ne libère-t-il jamais rien ?

Le piège. « Quand on a une fuite » n'est pas une réponse : l'intervieweur attend le mécanisme.

La réponse. shared_ptr compte ses propriétaires et libère l'objet quand le compteur tombe à zéro. Deux objets qui se possèdent mutuellement ne tombent jamais à zéro : c'est un cycle. La parade est std::weak_ptr, qui observe sans posséder (typiquement de l'enfant vers le parent).

#include <iostream>
#include <memory>

struct B;
struct A {
    std::shared_ptr<B> b;
    ~A() { std::cout << "~A\n"; }
};
struct B {
    std::shared_ptr<A> a;     // possède : cycle
    ~B() { std::cout << "~B\n"; }
};

int main() {
    {
        auto a = std::make_shared<A>();
        auto b = std::make_shared<B>();
        a->b = b;
        b->a = a;
        std::cout << "use_count a=" << a.use_count()
                  << " b=" << b.use_count() << "\n";
    }
    std::cout << "fin du scope\n";
}
g++ -std=c++23 -Wall -Wextra -pedantic -g -fsanitize=address q1.cpp -o q1 && ./q1

Ce que montre l'exécution. Sans sanitizer, le programme affiche :

use_count a=2 b=2
fin du scope

Aucun destructeur n'est appelé. Avec AddressSanitizer, LeakSanitizer signale 64 byte(s) leaked in 2 allocation(s).

La correction tient en une ligne dans B :

std::weak_ptr<A> a;       // B observe A sans le posséder

Sortie corrigée :

use_count a=1 b=2
~A
~B
fin du scope

Ce que l'intervieweur évalue. La compréhension du comptage de références, et la distinction entre posséder et observer. Pour aller plus loin : la page std::weak_ptr sur cppreference.

2. Une référence sur un élément de vector survit-elle à un push_back ?

Le piège. « Ça marche chez moi » : cela dépend de la capacité du vecteur au moment de l'appel.

La réponse. push_back peut réallouer le tableau interne. Les itérateurs, pointeurs et références vers les éléments deviennent alors invalides, et les utiliser est un comportement indéfini. Parades : reserve() en amont, conserver des indices plutôt que des références, ou ré-obtenir la référence après l'insertion.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v{1, 2, 3};
    int& premier = v[0];
    v.push_back(4);                 // réallocation : 'premier' pend dans le vide
    std::cout << premier << "\n";
}
g++ -std=c++23 -Wall -Wextra -pedantic -g -fsanitize=address q2.cpp -o q2 && ./q2

Ce que montre l'exécution. AddressSanitizer s'arrête avec ERROR: AddressSanitizer: heap-use-after-free, ligne 8, celle de l'affichage. Sans outil, le résultat est imprévisible.

Ce que l'intervieweur évalue. La connaissance des règles d'invalidation par conteneur, et le réflexe de lancer les sanitizers. Notre méthode complète de diagnostic est dans débogger un segfault.

3. string_view est-il toujours plus sûr que const string& ?

Le piège. string_view ne possède rien. Si la chaîne d'origine disparaît, la vue pointe dans le vide.

La réponse. C'est un couple pointeur et taille, sans propriété. Il se passe en paramètre de fonction sans copie, mais il ne se stocke pas au-delà de la vie des données, et il ne s'initialise jamais depuis une fonction qui retourne un std::string par valeur.

#include <iostream>
#include <string>
#include <string_view>

std::string fabrique() { return std::string(64, 'x'); }

int main() {
    std::string_view sv = fabrique();   // le temporaire meurt à la fin de la ligne
    std::cout << sv << "\n";            // sv pointe vers de la mémoire libérée
}
g++ -std=c++23 -Wall -Wextra -pedantic -g -fsanitize=address q5.cpp -o q5 && ./q5

Ce que montre l'exécution. AddressSanitizer signale un heap-use-after-free. La chaîne fait 64 caractères pour dépasser le petit tampon interne des chaînes courtes : elle alloue sur le tas, ce qui rend l'erreur détectable.

Ce que l'intervieweur évalue. La notion de propriété, et la capacité à reconnaître un temporaire qui meurt à la fin de la ligne. Référence : std::basic_string_view sur cppreference.


Sémantique et pièges du langage

4. std::move déplace-t-il quelque chose ?

Le piège. Non : std::move est un simple cast vers une référence rvalue. Le déplacement n'a lieu que si un constructeur ou un opérateur de déplacement est ensuite choisi.

La réponse. Sur un objet const, le cast produit une const T&&, qui ne peut pas se lier à T&&. C'est donc le constructeur de copie qui s'exécute, sans aucun message d'erreur.

#include <iostream>
#include <utility>

struct T {
    T() = default;
    T(const T&) { std::cout << "copy\n"; }
    T(T&&) noexcept { std::cout << "move\n"; }
};

int main() {
    T a;
    T b = std::move(a);       // T&& : constructeur de déplacement

    const T c{};
    T d = std::move(c);       // const T&& ne se lie pas à T&& : copie
    (void)b; (void)d;
}
g++ -std=c++23 -Wall -Wextra -pedantic q3.cpp -o q3 && ./q3

Ce que montre l'exécution.

move
copy

Ce que l'intervieweur évalue. La mécanique réelle (résolution de surcharge, références rvalue) plutôt que le slogan « move = rapide ». Référence : std::move sur cppreference.

5. vector<int> a(5, 1) et vector<int> b{5, 1} : combien d'éléments ?

La réponse. 5 pour a et 2 pour b. Les accolades privilégient le constructeur à std::initializer_list : b contient les deux valeurs 5 et 1, alors que a contient cinq fois la valeur 1.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> a(5, 1);   // 5 éléments valant 1
    std::vector<int> b{5, 1};   // 2 éléments : 5 et 1
    std::cout << a.size() << " " << b.size() << "\n";
}

Ce que montre l'exécution.

5 2

Ce que l'intervieweur évalue. La connaissance de l'initialisation uniforme et de ses exceptions, et la prudence qui consiste à choisir les parenthèses quand on veut une taille.

6. Pourquoi le destructeur d'une classe de base doit-il être virtual ?

La réponse. Détruire un objet dérivé via un pointeur vers sa base, sans destructeur virtuel, est un comportement indéfini. En pratique, le destructeur de la classe dérivée n'est pas appelé et ses ressources fuient.

#include <iostream>

struct Base {
    ~Base() { std::cout << "~Base\n"; }
    virtual void f() {}
};

struct Derived : Base {
    ~Derived() { std::cout << "~Derived\n"; }
};

int main() {
    Base* p = new Derived;
    delete p;
}
g++ -std=c++23 -Wall -Wextra -pedantic q4.cpp -o q4 && ./q4

Ce que montre l'exécution. -Wall prévient dès la compilation :

warning: deleting object of polymorphic class type 'Base' which has non-virtual destructor might cause undefined behavior [-Wdelete-non-virtual-dtor]

Le programme affiche seulement ~Base : le destructeur de Derived n'est pas appelé (comportement observé ici, non garanti par le standard). Avec virtual ~Base(), la sortie devient ~Derived puis ~Base.

Ce que l'intervieweur évalue. Le lien entre polymorphisme et destruction, et la nuance : une classe non polymorphe n'a pas besoin de destructeur virtuel.

7. Que se passe-t-il si un constructeur lance une exception ?

La réponse. L'objet n'est jamais considéré comme construit : son destructeur n'est pas appelé. En revanche, les sous-objets déjà construits (membres, classes de base) sont détruits. C'est la raison d'être du RAII : chaque ressource vit dans un membre qui se nettoie seul.

#include <iostream>
#include <stdexcept>

struct Membre {
    Membre()  { std::cout << "Membre()\n"; }
    ~Membre() { std::cout << "~Membre()\n"; }
};

struct Widget {
    Membre m;
    Widget() {
        std::cout << "Widget() debut\n";
        throw std::runtime_error("echec");
    }
    ~Widget() { std::cout << "~Widget()\n"; }
};

int main() {
    try {
        Widget w;
    } catch (const std::exception& e) {
        std::cout << "capture: " << e.what() << "\n";
    }
}
g++ -std=c++23 -Wall -Wextra -pedantic q8.cpp -o q8 && ./q8

Ce que montre l'exécution.

Membre()
Widget() debut
~Membre()
capture: echec

~Widget() n'apparaît jamais, alors que ~Membre() s'exécute.

Ce que l'intervieweur évalue. Le RAII et la sûreté face aux exceptions. Le piège classique est une ressource brute (new, fopen) acquise dans le constructeur sans objet qui la possède.


Comportement indéfini

8. Le compilateur a supprimé votre test : en a-t-il le droit ?

La règle. Un programme qui contient un comportement indéfini n'a plus de sens défini par le standard : le compilateur peut supposer que ce cas n'arrive jamais, et optimiser en conséquence.

Le cas qui casse. On déréférence un pointeur, puis on teste s'il est nul :

int lire_ou_defaut(const int* p) {
    int v = *p;              // on déréférence d'abord...
    if (p == nullptr)        // ...on teste ensuite : le compilateur peut supprimer ce test
        return -1;
    return v;
}

Compilé avec g++ -O2 -S, le test a disparu. Il ne reste que deux instructions utiles :

_Z14lire_ou_defautPKi:
	endbr64                  ; protection CET, activée par défaut sur Ubuntu
	movl	(%rdi), %eax
	ret

À -O0, on retrouve bien cmpq $0, -24(%rbp) puis un saut conditionnel. Le compilateur raisonne ainsi : si p était nul, le déréférencement précédent serait déjà indéfini, donc p n'est pas nul.

Même programme, même entrée, réponses différentes. Avec ce second exemple, x + 1 > x pour x = INT_MAX (débordement d'entier signé) :

#include <climits>
#include <iostream>

bool toujours_vrai(int x) { return x + 1 > x; }

int main() {
    std::cout << toujours_vrai(INT_MAX) << "\n";
}
Compilateur Option Affiche
g++ 13.3 -O0 1
g++ 13.3 -O2 1
clang++ 18.1 -O0 0
clang++ 18.1 -O2 1

La parade. Ne jamais compter sur le débordement d'un entier signé : vérifier avant d'ajouter, ou utiliser des types non signés pour l'arithmétique modulaire. Compiler les tests avec -fsanitize=undefined aide, mais ne prouve rien : UBSan n'a rien signalé sur ce petit programme, car l'expression est résolue avant l'exécution. Un outil n'est pas une preuve d'absence de comportement indéfini.

Ce que l'intervieweur évalue. Que vous sachiez que « ça marche sur ma machine » n'est pas une preuve, et que vous connaissiez les outils et leurs limites. Dans les secteurs certifiés, ce réflexe fait partie du métier : voir DO-178C, IEC 62304, ISO 26262 : pourquoi les outils ne suffisent pas. Référence : comportement indéfini sur cppreference.


Concurrence et embarqué

9. volatile rend-il un accès thread-safe ?

La réponse. Non. volatile interdit au compilateur de supprimer ou de regrouper les accès (utile pour les registres mappés en mémoire en embarqué), mais ne garantit ni l'atomicité ni l'ordre entre threads. Pour partager un compteur, il faut std::atomic ou un mutex.

#include <atomic>
#include <iostream>
#include <thread>

volatile int compteur_volatile = 0;
std::atomic<int> compteur_atomique{0};

void travail() {
    for (int i = 0; i < 100000; ++i) {
        compteur_volatile = compteur_volatile + 1;   // lecture puis écriture : pas atomique
        compteur_atomique.fetch_add(1);
    }
}

int main() {
    std::thread t1(travail), t2(travail);
    t1.join();
    t2.join();
    std::cout << "volatile: " << compteur_volatile
              << "  atomique: " << compteur_atomique << "\n";
}
g++ -std=c++23 -Wall -Wextra -pedantic -O2 -pthread q9.cpp -o q9 && ./q9

Ce que montre l'exécution. Deux threads incrémentent chacun 100 000 fois : on attend 200 000. Sur 5 exécutions, le compteur volatile a donné entre 122 445 et 157 056, et le compteur atomique 200 000 à chaque fois. Avec -fsanitize=thread, ThreadSanitizer signale un data race à la ligne de l'incrément volatile.

Ce que l'intervieweur évalue. Que vous distinguiez les deux usages de volatile : légitime pour le matériel, inutile pour la synchronisation. Pour approfondir : multithreading en C++ : threads, mutex, async.

10. Deux mutex verrouillés dans deux ordres différents : que se passe-t-il ?

La réponse. Si un thread prend m1 puis m2 et que l'autre prend m2 puis m1, chacun peut attendre indéfiniment le verrou détenu par l'autre : c'est un interblocage. Parades : imposer un ordre global, ou verrouiller les deux d'un coup avec std::scoped_lock (C++17), qui évite l'interblocage.

#include <iostream>
#include <mutex>
#include <thread>

std::mutex m1, m2;
long partage = 0;

void f() {
    for (int i = 0; i < 1000000; ++i) {
        m1.lock(); m2.lock();               // ordre : m1 puis m2
        ++partage;
        m2.unlock(); m1.unlock();
    }
}

void g() {
    for (int i = 0; i < 1000000; ++i) {
        m2.lock(); m1.lock();               // ordre : m2 puis m1 -> interblocage possible
        ++partage;
        m1.unlock(); m2.unlock();
    }
}

int main() {
    std::thread a(f), b(g);
    a.join();
    b.join();
    std::cout << partage << "\n";
}

Avec std::scoped_lock, les deux verrouillages deviennent :

std::scoped_lock lk(m1, m2);   // dans f()
std::scoped_lock lk(m2, m1);   // dans g() : l'ordre n'a plus d'importance
g++ -std=c++23 -Wall -Wextra -pedantic -O2 -pthread q10.cpp -o q10 && timeout 8 ./q10

Ce que montre l'exécution. Avec 1 000 000 d'itérations par thread, la version à verrouillage manuel n'a pas terminé en 8 secondes (code de retour 124 de timeout). La version scoped_lock a terminé et affiché 2 000 000, soit 2 × 1 000 000. L'interblocage dépend de l'ordonnancement : il s'est produit ici sur 2 cœurs.

Ce que l'intervieweur évalue. La distinction entre course aux données et interblocage, et la connaissance de l'outil standard qui évite le second.

11. Comment gérer les erreurs sans exceptions ?

La réponse. Beaucoup de projets embarqués ou temps réel compilent avec -fno-exceptions. Les options : codes de retour (avec [[nodiscard]]), std::optional pour l'absence de valeur, et std::expected<T, E> (C++23) qui porte soit la valeur, soit l'erreur, et oblige à traiter les deux cas.

#include <charconv>
#include <expected>
#include <iostream>
#include <string>
#include <string_view>

std::expected<int, std::string> analyser(std::string_view s) {
    int v{};
    auto [fin, ec] = std::from_chars(s.data(), s.data() + s.size(), v);
    if (ec != std::errc{} || fin != s.data() + s.size())
        return std::unexpected("pas un entier");
    return v;
}

int main() {
    for (std::string_view s : {"42", "abc", "12x"}) {
        if (auto r = analyser(s))
            std::cout << s << " -> " << *r << "\n";
        else
            std::cout << s << " -> erreur: " << r.error() << "\n";
    }
}
g++ -std=c++23 -Wall -Wextra -pedantic -fno-exceptions q11.cpp -o q11 && ./q11

Ce que montre l'exécution (compilé avec GCC 13.3, sans exceptions) :

42 -> 42
abc -> erreur: pas un entier
12x -> erreur: pas un entier

Ce que l'intervieweur évalue. Le pragmatisme : savoir pourquoi un projet interdit les exceptions (taille du binaire, déterminisme) et quel outil choisir à la place. Voir aussi C++ et IA embarquée pour le contexte embarqué. Référence : std::expected sur cppreference.


Qt : threads et signaux

12. Un thread de travail émet un signal connecté à un slot d'une fenêtre : dans quel thread le slot s'exécute-t-il ?

La règle (documentation Qt 6). Avec Qt::AutoConnection, le mode par défaut, la connexion est directe si le récepteur vit dans le thread qui émet le signal, sinon elle est mise en file d'attente. Le type est déterminé au moment de l'émission. En Qt::DirectConnection, le slot s'exécute dans le thread émetteur ; en Qt::QueuedConnection, il s'exécute dans le thread du récepteur, via sa boucle d'événements.

Les pièges.

  • Toucher un widget depuis le thread de travail : la documentation est nette, les classes d'interface « ne peuvent être utilisées que depuis le thread principal ».
  • Connecter une lambda sans objet de contexte : vous ne précisez pas dans quelle boucle d'événements elle doit s'exécuter. Avec un objet de contexte en troisième argument, la lambda est placée dans la boucle d'événements de ce contexte, et la connexion est coupée automatiquement si le contexte ou l'émetteur est détruit (documentation de QObject::connect).
// Extrait illustratif, non exécuté pour cet article.
// 'worker' vit dans un thread de travail, 'fenetre' dans le thread GUI.
QObject::connect(worker, &Worker::resultatPret,
                 fenetre, &Fenetre::afficher,
                 Qt::QueuedConnection);   // exécuté dans le thread de 'fenetre'

Ce que l'intervieweur évalue. L'affinité de thread des QObject et la compréhension de la boucle d'événements, deux points centraux du modèle d'objets de Qt. Pour aller plus loin : l'énumération Qt::ConnectionType, notre page expertise Qt et l'article Qt 6.10 : C++ et QML.


Se préparer en 7 jours

Jour Thème Questions À faire
1 Durée de vie 1, 2, 3 Compiler chaque exemple avec -fsanitize=address, puis le casser autrement
2 Sémantique 4, 5 Écrire une classe avec règle des cinq et tracer copies et déplacements
3 Destruction et exceptions 6, 7 Remplacer une ressource brute par un objet RAII
4 Comportement indéfini 8 Comparer l'assembleur à -O0 et -O2 sur Compiler Explorer
5 Concurrence 9, 10 Provoquer une course aux données et la voir avec -fsanitize=thread
6 C++ moderne, embarqué ou Qt 11, 12 Réécrire une fonction à exceptions avec std::expected
7 Simulation orale 1 à 12 Expliquer chaque piège à voix haute en trois temps

Commandes de base :

# Mémoire et comportement indéfini (AddressSanitizer inclut LeakSanitizer sous Linux)
g++ -std=c++23 -Wall -Wextra -pedantic -g -fsanitize=address,undefined prog.cpp -o prog

# Concurrence (ThreadSanitizer ne se combine pas avec AddressSanitizer)
g++ -std=c++23 -Wall -Wextra -pedantic -g -fsanitize=thread -pthread prog.cpp -o prog

Une équipe veut monter en compétence collectivement ? Nous proposons des formations C++ et Qt sur mesure.


5 questions à poser au recruteur

Un entretien va dans les deux sens. Ces questions montrent que vous raisonnez comme un ingénieur et vous renseignent sur le projet :

  1. Quel standard C++ et quel compilateur sont imposés sur le projet ?
  2. Quels outils d'analyse (sanitizers, clang-tidy, analyse statique) tournent en intégration continue ?
  3. Comment sont organisées les revues de code ?
  4. Quelle est la part de code historique, et existe-t-il des tests dessus ?
  5. Comment se passe la montée en compétence sur le domaine métier (médical, aéronautique, simulation) ?

Postuler chez PointerLab, ou recruter un profil C++

PointerLab est une ESN spécialisée en C++ et Qt, partenaire officiel Qt Group, qui intervient du C++98 au C++20 dans le médical, l'aéronautique, l'automobile, l'industrie et la simulation 3D. Notre modèle repose sur la transparence : la grille de salaires est publique, de 45 000 € brut à 1 an d'expérience à 71 000 € à 10 ans, et un simulateur permet de projeter sa situation.


FAQ

Quelles questions pose-t-on en entretien technique C++ ?

Les entretiens mêlent des questions de fond sur la durée de vie des objets (pointeurs intelligents, références, string_view), la sémantique de déplacement, le comportement indéfini, la concurrence (mutex, atomiques) et, selon le poste, l'embarqué ou Qt. Les questions de ce guide suivent ce découpage, avec un code testé pour chacune.

Faut-il connaître C++20 ou C++23 pour un entretien ?

Cela dépend du projet. Beaucoup de bases de code embarquées ou industrielles restent en C++11, 14 ou 17, et PointerLab intervient du C++98 au C++20. Les fondamentaux (durée de vie, déplacement, comportement indéfini) sont en général plus décisifs que les nouveautés, mais savoir situer std::expected, les concepts ou la réflexion de C++26 est un plus.

Comment préparer un entretien C++ en une semaine ?

Travaillez un thème par jour (mémoire, langage, comportement indéfini, concurrence, C++ moderne, embarqué ou Qt), compilez chaque exemple avec -Wall -Wextra et les sanitizers, puis terminez par une simulation orale : expliquer chaque piège à voix haute en trois temps, la règle, le cas où elle casse et la façon de le détecter.

Quels outils utiliser pour tester ses réponses ?

GCC ou Clang avec -Wall -Wextra -pedantic, AddressSanitizer et LeakSanitizer (-fsanitize=address), ThreadSanitizer (-fsanitize=thread, à ne pas combiner avec AddressSanitizer), et Compiler Explorer (godbolt.org) pour lire l'assembleur généré à chaque niveau d'optimisation.

Qt est-il demandé en entretien C++ ?

Pour les postes d'IHM, d'embarqué ou d'applications scientifiques en Qt, oui : affinité de thread, boucle d'événements, signaux et slots, types de connexion, modèles et vues. La question 12 de ce guide traite d'un point clé du modèle d'objets de Qt : le thread d'exécution d'un slot.


Sources

Rejoignez la communauté C++ 🇫🇷 sur Discord !

Un espace convivial pour échanger et apprendre ensemble.