Por que o C++ ainda assusta até mesmo os seniores?
\nC++ é uma linguagem que lhe dá controle total sobre a memória, mas exatamente até você esquecer de colocar um ponto e vírgula ou acidentalmente chamar uma função virtual a partir de um construtor. As entrevistas de C++ são famosas por sua crueldade: podem perguntar sobre campos de bits, sobre a estrutura da tabela virtual e sobre os detalhes de std::move. Se você está se preparando para uma entrevista técnica, este artigo é sua lista de verificação. Analisaremos os tópicos mais traiçoeiros que quase certamente surgirão na entrevista e mostraremos como respondê-los para não ser reprovado.
No portal educacional PythonLib, frequentemente discutimos como fazer a transição de Python para C++ e vice-versa. Mas hoje é C++ puro. Sem concessões.
\n\n1. Funções virtuais e tabelas VTable: como não cair na armadilha
\nPergunta: "O que acontece se você chamar uma função virtual a partir do construtor da classe base?" — um clássico do gênero. 70% dos candidatos respondem incorretamente. Vamos entender.
\n\nPor que isso é perigoso?
\nQuando você chama uma função virtual no construtor da classe base, o objeto da classe derivada ainda não foi criado. A tabela virtual (VTable) neste momento aponta para os métodos da classe base, não para os da classe derivada. Como resultado, o polimorfismo não funciona.
\n\n#include <iostream>\n\nclass Base {\npublic:\n Base() { callFoo(); } // Chamada no construtor\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; // Imprimirá: Base::foo, não Derived::foo\n return 0;\n}\n\nConclusão: nunca chame funções virtuais em construtores e destrutores. Se o entrevistador perguntar — explique com confiança o mecanismo da VTable.
\n\n2. Ponteiros inteligentes: RAII e gerenciamento de memória
\nNenhuma entrevista séria de C++ fica sem este tópico. Perguntas sobre std::unique_ptr, std::shared_ptr e std::weak_ptr são básicas. Mas frequentemente perguntam sobre referências cíclicas e std::enable_shared_from_this.
Como evitar um vazamento com shared_ptr?
\nImagine que você tem dois objetos que se referenciam mutuamente através de shared_ptr. O contador de referências nunca será zerado — a memória vazará. Solução: um dos ponteiros deve ser weak_ptr.
#include <memory>\n#include <iostream>\n\nstruct Node {\n std::shared_ptr<Node> next;\n std::weak_ptr<Node> prev; // Referência fraca para evitar ciclo\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 não incrementa o contador\n // Ao sair do escopo, ambos os nós serão deletados corretamente\n return 0;\n}\n\nNa entrevista, mencione obrigatoriamente o conceito RAII (Resource Acquisition Is Initialization) — é a filosofia do C++ que vincula o tempo de vida do recurso ao tempo de vida do objeto.
\n\n3. Move-semântica e std::move: mágica ou truque?
\nPergunta típica: "O que std::move faz?" A resposta "move o objeto" está incorreta. std::move apenas converte o tipo para uma rvalue-reference. A movimentação real ocorre no construtor de movimentação ou no operador de atribuição de movimentação.
Exemplo para entrevista
\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 // Construtor de movimentação\n MyString(MyString&& other) noexcept : data(std::move(other.data)) {\n std::cout << "Move constructor c