Alocação sem construção
2024-06-11 computer blogMeu amigo está mexendo com allocator e fez o seguinte teste, o que pode dar a ideia errada para o iniciante, porque testei aqui e deu tudo certo:
#include <iostream>
struct A {
A() = default;
uint64_t x;
};
int main()
{
auto alloc = std::allocator<A>();
A* p = alloc.allocate(2);
p[1].x = 10;
}
Porém, ele não havia revelado que a “struct” que ele usou não era dessas sem complexidade alguma, mas a std::string, que depende da construção porque possui membros que controlam o estado do objeto. E aí sim apenas alocar o espaço na memória não é suficiente. É preciso construir o objeto chamando o construtor.
#include <iostream>
#include <string>
struct A {
A() = default;
uint64_t x;
std::string y; // new member
};
int main()
{
auto alloc = std::allocator<A>();
A* p = alloc.allocate(2);
p[1].y = "y"; // crash
}
Ao depurar a chamada ao operator = que chama por sua vez o método std::string::assign é possível ver que o objeto está com os membros size e capacity com lixo. O que faz sentido, já que o construtor dessa string nunca foi chamado.
- this 0x00000216888f5858 <Error reading characters of string.> std::string * [size] 14829735431805717965 unsigned __int64 [capacity] 14829735431805717965 unsigned __int64 + [allocator] allocator std::_Compressed_pair< std::allocator<char>,std::_String_val<std::_Simple_types<char>>,1> [0] <Unable to read memory> char
Para corrigir isso podemos usar o que C++ chama de placement new: uma construção de objeto em memória previamente disponível.
A* p = alloc.allocate(2); new (p) A[2]; // remember to use new array operator!