Última actualización:2026-10-12
C++ Evitar el constructor por defecto¶
En C++, algunos contenedores invocan el constructor por defecto cuando se ajusta su tamaño mediante resize(), lo cual puede no ser el comportamiento que deseamos. Este artículo explica cómo controlar ese proceso con un asignador (allocator) personalizado para evitar la llamada a la construcción por defecto.
Ejemplo 1:
¿Sabes qué se imprime en este momento?
La respuesta es:
Aquí hay varios puntos que conviene entender:
Primero: ¿por qué los datos volvieron a ser 0 tras el resize? ¿Qué ha ocurrido?
Segundo: ¿qué hacer si quiero conservar los datos originales? Es decir, quiero que el resultado sea:
Tercero: ¿qué ocurre si los miembros del vector no tienen constructor por defecto?
Cuarto: ¿qué limitaciones tiene resize?
En esta sección estudiaremos esta potente funcionalidad: cómo conseguir que resize evite llamar al constructor por defecto.
Nota: el espacio es limitado; todo el código de la versión abreviada de esta sección se actualiza en el Planeta de Conocimiento.
1.Análisis de resize¶
Para que este artículo fluya mejor, utilicé el siguiente ejemplo: se prohíbe la construcción de Foo y después se aplica resize sobre él en un vector.
Esto da un error de compilación y podemos ver un montón de mensajes de error.
Primer problema: se observa que la pila de llamadas llega a __uninitialized_default_n, donde se comprueba si el tipo actual es un tipo trivial; ¡esta es la primera limitación de resize!
required from '_ForwardIterator std::__uninitialized_default_n(_ForwardIterator, _Size) [with _ForwardIterator = Foo*; _Size = long unsigned int]'
710 | return __uninitialized_default_n_1<__is_trivial(_ValueType)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
711 | && __can_fill>::
Segundo problema: como hemos prohibido la construcción por defecto, el asignador falla al hacer placement new de un objeto por defecto en la dirección original, y ahí se produce el error; ¡esta es la segunda limitación!
gcc/14.1.0_1/include/c++/14/bits/stl_construct.h:119:7: error: use of deleted function 'Foo::Foo()'
119 | ::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
| ^~~~~~~~~~~~~~~~~~~
Dicho esto, ya podemos entrar en materia: se ve que resize usa el asignador para reservar memoria y, cuando el tamaño pasado es mayor que size (asumiendo que estamos dentro de capacity, es decir, que la dirección es válida), emplea el asignador para hacer placement new de un objeto construido por defecto.
Os dejo aquí la implementación en el código fuente de gcc para que la veáis; sencillo, ¿verdad? Solo llama a construct.
__try
{
typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
for (; __n > 0; --__n, (void)++__cur)
__traits::construct(__alloc, std::__addressof(*__cur));
return __cur;
}
__catch(...)
{
std::_Destroy(__first, __cur, __alloc);
__throw_exception_again;
}
Bien, ya explicado el principio de resize, pasemos a la pregunta del principio.
2.Evitar la llamada al constructor por defecto¶
¿Cómo evitar la llamada al constructor por defecto?
En realidad es muy sencillo: basta con bloquear el construct que gcc aplica por defecto. ¿Y cómo se bloquea?
Aquí entra en juego el segundo parámetro de vector: ¡el asignador!
3.Asignador¶
Para resolver este problema podemos usar un asignador personalizado. Un asignador personalizado permite controlar el comportamiento de construcción de los objetos, dándonos a elegir si se usa o no la construcción por defecto al llamar a resize. A continuación, una implementación sencilla de un asignador personalizado; solo se incluye el código esencial.
template <class T>
class no_init_alloc {
public:
using value_type = T;
// Override construct method to do nothing
template <class U>
void construct(U* p) {
// Do nothing, memory is not initialized
}
};
Basta con que construct no haga nada; entonces ya podemos usarlo, es decir:
Con esto se obtiene:
en lugar de:
Bien, ¿lo has aprendido?
Fin de esta sección
Temas relacionados¶
- C++ Idioma de la lista de inicialización de clases | C++那些事
- C++: plantillas de clase antes que plantillas de función | C++那些事
- C++ Idioma copy-and-swap | C++那些事
- C++ Inyección de dependencias | C++那些事
- C++ enum class enumeraciones fuertemente tipadas | C++那些事
- C++ Header-Only frente a separación de implementación | C++那些事
- C++ Idioma de evaluación perezosa | C++那些事
- C++ Idioma de la tabla de consulta | C++那些事
- C++ Punteros a miembros e información de tipos | C++那些事
- C++ Idioma mixin | C++那些事