Cómo aprobar una entrevista de C++: preguntas avanzadas principales
Una entrevista para un puesto de desarrollador de C++ siempre es un desafío. Si bien probablemente conozcas las preguntas básicas (diferencia entre new y malloc, qué es un destructor virtual), el nivel avanzado requiere una comprensión profunda del funcionamiento interno del lenguaje. En este artículo, analizaremos los temas clave que seguramente surgirán en una entrevista técnica para posiciones Middle y Senior. No solo enumeraremos las preguntas, sino que también proporcionaremos respuestas detalladas con ejemplos de código.
1. Punteros inteligentes y su funcionamiento interno
Pregunta: "Explica la diferencia entre std::unique_ptr y std::shared_ptr. ¿Cómo se implementa la seguridad de hilos del contador de referencias?"
Esto es un clásico. El entrevistador quiere ver que entiendes no solo la sintaxis, sino también la semántica de la propiedad de los recursos.
- std::unique_ptr — propiedad exclusiva. No se copia, solo se mueve. Ideal para recursos que tienen un solo propietario.
- std::shared_ptr — propiedad compartida. Utiliza un contador de referencias (bloque de control). Cuando el contador llega a cero, el recurso se destruye.
- std::weak_ptr — "observador". No incrementa el contador de referencias. Se utiliza para romper dependencias cíclicas en
shared_ptr.
Matiz avanzado: El contador de referencias en shared_ptr es seguro para hilos (se utilizan operaciones atómicas), pero esto no hace que el objeto gestionado en sí sea seguro para hilos. También existen dos contadores: de referencias fuertes (strong) y de referencias débiles (weak). Mientras haya al menos una referencia débil, el bloque de control no se elimina, incluso si el objeto ya ha sido destruido.
#include <memory>#include <iostream>
class Resource {public: Resource() { std::cout << "Resource acquired"; } ~Resource() { std::cout << "Resource destroyed"; } void doWork() { std::cout << "Working..."; }};
int main() { std::unique_ptr<Resource> uptr = std::make_unique<Resource>(); uptr->doWork(); // std::unique_ptr<Resource> uptr2 = uptr; // ¡Error de compilación! std::unique_ptr<Resource> uptr2 = std::move(uptr); // Ok
std::shared_ptr<Resource> sptr1 = std::make_shared<Resource>(); std::shared_ptr<Resource> sptr2 = sptr1; // Contador de referencias = 2 sptr1.reset(); // Contador = 1 sptr2.reset(); // Contador = 0, objeto destruido return 0;}2. Tablas virtuales (vtable) y mecanismo de enlace tardío
Pregunta: "¿Cómo funciona una función virtual a nivel de memoria? ¿Dónde se almacena la vtable?"
Esta pregunta evalúa la comprensión no solo del polimorfismo, sino también de la organización de la memoria en C++.
- Para cada clase que tenga al menos una función virtual, el compilador crea una tabla estática (vtable). Contiene punteros a las funciones virtuales.
- Cada objeto de dicha clase contiene un puntero oculto
__vptr, que apunta a la vtable de su clase. - Al llamar a una función virtual a través de un puntero o referencia, el compilador genera código que sigue el
__vptrhasta la vtable, encuentra la función correspondiente por desplazamiento y la invoca. - La vtable se almacena en memoria estática (como una variable global), no en la pila ni en el montón.
Matiz avanzado: Complejidades con la herencia múltiple. En este caso, un objeto puede tener varios __vptr, y surge la necesidad de funciones thunk (ajuste del puntero this). Esta es una de las razones por las que la herencia múltiple en C++ se considera peligrosa y lenta.
#include <iostream>
class Base {public: virtual void foo() { std::cout << "Base::foo"; } virtual ~Base() = def