Como passar em uma entrevista de C++: principais perguntas de nível avançado
\n\nA entrevista para uma posição de desenvolvedor C++ de nível médio e sênior não é apenas uma verificação de sintaxe. Os entrevistadores querem ver uma compreensão profunda do funcionamento interno da linguagem, habilidade para trabalhar com memória, templates e multithreading. Neste artigo, analisamos as perguntas mais frequentes e difíceis que são feitas em entrevistas de C++ de nível avançado. Cada pergunta é acompanhada de uma explicação e um exemplo de código.
\n\n1. Ponteiros inteligentes e RAII
\n\nA pergunta sobre ponteiros inteligentes é um clássico. Espera-se que você não apenas nomeie std::unique_ptr, std::shared_ptr e std::weak_ptr, mas também explique seu funcionamento interno e quando usar cada um.
Pergunta típica: Qual é a diferença entre unique_ptr e shared_ptr? Como funciona o contador de referências em shared_ptr? Para que serve o weak_ptr?
Resposta: std::unique_ptr é a propriedade exclusiva de um recurso. Ele não pode ser copiado, apenas movido (move semantics). std::shared_ptr usa contagem de referências (reference counting) para propriedade compartilhada. Quando o contador chega a zero, o objeto é deletado. std::weak_ptr é um "observador" que não incrementa o contador de referências. Ele é usado para evitar dependências cíclicas (por exemplo, em grafos ou árvores).
#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; // Erro de compilação - cópia proibida\n std::unique_ptr<Resource> ptr2 = std::move(ptr1); // Movimento permitido\n\n // shared_ptr\n std::shared_ptr<Resource> sptr1 = std::make_shared<Resource>();\n std::shared_ptr<Resource> sptr2 = sptr1; // Cópia permitida, contador = 2\n\n // weak_ptr\n std::weak_ptr<Resource> wptr = sptr1; // Contador não é incrementado\n if (auto locked = wptr.lock()) {\n std::cout << "Resource is still alive\\n";\n }\n\n return 0;\n}\n\nPergunta adicional: Como implementar seu próprio SharedPtr? Aqui é importante mencionar RAII (Resource Acquisition Is Initialization) — o princípio onde a aquisição do recurso ocorre no construtor e a liberação no destrutor.
2. Destrutores virtuais e tabela de funções virtuais (vtable)
\n\nEsta pergunta testa a compreensão de polimorfismo e o funcionamento das funções virtuais em C++.
\n\nPergunta típica: Por que em uma classe base com funções virtuais o destrutor deve ser virtual? Como funciona a vtable?
\n\nResposta: Se o destrutor da classe base não for virtual, ao deletar um objeto de uma classe derivada através de um ponteiro para a classe base, apenas o destrutor da classe base será chamado. Isso causará vazamento de recursos que a classe derivada alocou.
\n\nCada classe que possui pelo menos uma função virtual contém uma tabela estática (vtable). Nela são armazenados ponteiros para as implementações das funções virtuais. Cada objeto de tal classe possui um ponteiro oculto (vptr) que aponta para a vtable de sua classe. A chamada de uma função virtual ocorre através de dupla indireção: objeto → vptr → vtable → função. Isso adiciona uma pequena sobrecarga em comparação com uma chamada normal.
\n\n#include <iostream>\n\nclass Base {\npublic:\n virtual void show() { std::cout << "Base\\n"; }\n virtual ~Base() = default; // Destrutor virtual\n};\n\nclass Derived : public Base {\npublic:\n void show() override { std::cout << "Deriv