Comment réussir un entretien C++ : les meilleures questions de niveau avancé
\n\nUn entretien pour un poste de développeur C++ de niveau intermédiaire ou senior ne se limite pas à une simple vérification de la syntaxe. Les recruteurs souhaitent voir une compréhension approfondie du fonctionnement interne du langage, la capacité à gérer la mémoire, les templates et le multithreading. Dans cet article, nous avons rassemblé les questions les plus fréquentes et les plus difficiles posées lors des entretiens C++ de niveau avancé. Chaque question est accompagnée d'une explication et d'un exemple de code.
\n\n1. Pointeurs intelligents et RAII
\n\nLa question sur les pointeurs intelligents est un classique. On attend de vous non seulement de nommer std::unique_ptr, std::shared_ptr et std::weak_ptr, mais aussi d'expliquer leur fonctionnement interne et quand les utiliser.
Question typique : Quelle est la différence entre unique_ptr et shared_ptr ? Comment fonctionne le compteur de références dans shared_ptr ? À quoi sert weak_ptr ?
Réponse : std::unique_ptr représente une propriété exclusive d'une ressource. Il ne peut pas être copié, seulement déplacé (move semantics). std::shared_ptr utilise un comptage de références (reference counting) pour une propriété partagée. Lorsque le compteur tombe à zéro, l'objet est supprimé. std::weak_ptr est un "observateur" qui n'augmente pas le compteur de références. Il est utilisé pour éviter les dépendances cycliques (par exemple, dans les graphes ou les arbres).
#include <memory>\n#include <iostream>\n\nstruct Resource {\n Resource() { std::cout << "Resource acquired\\n"; }\n ~Resource() { std::cout << "Resource released\\n"; }\n};\n\nint main() {\n // unique_ptr\n std::unique_ptr<Resource> ptr1 = std::make_unique<Resource>();\n // std::unique_ptr<Resource> ptr2 = ptr1; // Erreur de compilation - copie interdite\n std::unique_ptr<Resource> ptr2 = std::move(ptr1); // Déplacement autorisé\n\n // shared_ptr\n std::shared_ptr<Resource> sptr1 = std::make_shared<Resource>();\n std::shared_ptr<Resource> sptr2 = sptr1; // Copie autorisée, compteur = 2\n\n // weak_ptr\n std::weak_ptr<Resource> wptr = sptr1; // Le compteur n'augmente pas\n if (auto locked = wptr.lock()) {\n std::cout << "Resource is still alive\\n";\n }\n\n return 0;\n}\n\nQuestion complémentaire : Comment implémenter votre propre SharedPtr ? Il est important de mentionner RAII (Resource Acquisition Is Initialization) — le principe selon lequel l'acquisition d'une ressource se fait dans le constructeur et sa libération dans le destructeur.
2. Destructeurs virtuels et table des fonctions virtuelles (vtable)
\n\nCette question teste la compréhension du polymorphisme et du fonctionnement des fonctions virtuelles en C++.
\n\nQuestion typique : Pourquoi une classe de base avec des fonctions virtuelles doit-elle obligatoirement avoir un destructeur virtuel ? Comment fonctionne la vtable ?
\n\nRéponse : Si le destructeur de la classe de base n'est pas virtuel, lors de la suppression d'un objet d'une classe dérivée via un pointeur sur la classe de base, seul le destructeur de la classe de base sera appelé. Cela entraînera une fuite des ressources allouées par la classe dérivée.
\n\nChaque classe possédant au moins une fonction virtuelle contient une table statique (vtable). Celle-ci stocke les pointeurs vers les implémentations des fonctions virtuelles. Chaque objet d'une telle classe possède un pointeur caché (vptr) qui pointe vers la vtable de sa classe. L'appel d'une fonction virtuelle se fait par une double indirection : objet → vptr → vtable → fonction. Cela ajoute un léger surcoût par rapport à un appel normal.
\n\n#include <iostream>\n\nclass Base {\npublic:\n virtual void show() { std::cout << "Base\\n"; }\n virtual ~Base() = default; // Destructeur virtuel\n};\n\nclass Derived : public Base {\npublic:\n void show() override { std::cout << "Deriv