Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

A instanciação de string nunca chama o operador new sobrecarregado.

A aula dá a entender que, sendo a string grande o suficiente, ela é alocada na heap, e portanto invoca o operador new. Mas eu não vejo isso ocorrendo quando eu rodo o código aqui:

#include <iostream>
#include <string>

using namespace std;

void* operator new (size_t bytes){
    cout<< "Alocando " << bytes <<" bytes"<<endl;
    return malloc(bytes);
}

int main(){
    string str1 = "1234567890234567890123";
    string* str2 = new string("1234567890234567890123");
    return 0;
}

Não importa o tamanho do texto que eu ponha em str1, ele nunca vai entrar no operator new. Ela só entra quando eu instancio como um ponteiro, como é feito com str2. E também não importa o tamanho do texto que eu ponha em str2, ele sempre informa que alocou 32 bytes. Isso também é coisa de compilador/arquitetura? (estou compilando no VS code usando g++, em Windows 64 bits)

5 respostas

Olá, Daniel. Tudo bem?

A sua dúvida é bastante interessante e está relacionada com um conceito chamado "Small String Optimization" (SSO). O que acontece é que, em muitas implementações da classe std::string, há uma otimização que evita a alocação de memória na heap para strings pequenas. Em vez disso, elas são armazenadas diretamente na stack, o que é mais eficiente em termos de performance.

No seu exemplo, quando você cria str1, a string é pequena o suficiente para ser armazenada na stack, então o operador new não é chamado. Já quando você cria str2 com new, você está explicitamente pedindo para alocar memória na heap, e é por isso que o operador new é chamado, independentemente do tamanho da string.

Essa otimização depende do compilador e da arquitetura. No seu caso, com o g++ em um sistema Windows 64 bits, parece que strings com até 23 caracteres (ou algo próximo) são otimizadas para a stack. Isso pode variar conforme o compilador e a versão que você está usando.

Espero ter ajudado e bons estudos.

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.Bons Estudos!

Sim, essa parte eu entendi. A minha dúvida é porque quando eu faço aqui, mesmo uma string com muito mais de 23 caracteres não chama o operator new sobrecarregado, e mesmo quando eu chamo ele explicitamente, a função sempre printa que alocou 32 bytes. Achei estranho porque é bem diferente do comportamento mostrado na aula. Acabei jogando essa dúvida no ChatGPT para ver como ele responde, e pela resposta, basicamente o que acontece na realidade de fato não tem nada a ver com o que é mostrado na aula. Resumidamente:

  • Instanciar com std::string não chama o operador new diretamente, mas sim delega o gerenciamento de memória para um allocator (ou seja, std::allocator). Este aloca a memória com malloc() ou alguma outra estratégia de alocação, sem chamar o operador new em lugar algum.

  • Quando eu aloco chamando o new diretamente, o que é controlado no parâmetro da função sobrecarregada é o tamanho do tipo/objeto string (que em plataformas de 64 bits, tem o tamanho fixo de 32 bytes), não o armazenamento interno de caracteres. Este é controlado em um buffer separado, alocado com malloc ao invés de new.

Faz sentido? Eis a conversa, para referência: https://chatgpt.com/share/67cb36a6-a7e8-8012-931e-3707099d37a6

Olá, Daniel.

Sobre sua última dúvida, você está certo ao notar que std::string não chama diretamente o operador new sobrecarregado, e isso acontece porque a alocação de memória da string é gerenciada internamente por um allocator (std::allocator).

Quando você instancia um objeto std::string, o armazenamento da string pode ser feito de duas formas:

  1. Small String Optimization (SSO): Se a string for pequena, os caracteres são armazenados diretamente dentro do próprio objeto std::string, sem necessidade de alocação dinâmica.
  2. Heap Allocation: Se a string for maior que o limite do SSO, um buffer separado é alocado dinamicamente pelo allocator, geralmente usando malloc() ou alguma estratégia interna, sem passar pelo operador new sobrecarregado.

No seu código, quando você cria str1, mesmo com uma string longa, o operador new sobrecarregado não é chamado porque a std::string está gerenciando a alocação com seu próprio mecanismo interno. Já ao criar str2 com new, você está alocando um objeto std::string na heap, mas isso apenas aloca o espaço necessário para o objeto, e não para os caracteres da string, que são alocados separadamente pelo allocator.

Se quiser verificar como o armazenamento interno está sendo feito, você pode tentar debugar o código ou usar std::string::capacity() para ver o tamanho do buffer alocado.

Fico à disposição. Abraços e bons estudos!

Certo, mas com tudo isso esclarecido, ainda ficam as dúvidas de por que, na demonstração do Vinicius, a função sobrecarregada é chamada quando se cria uma string com std::string? E por que, ao ser chamada, ela imprime uma quantidade de bytes variável dependendo do tamanho do texto ao invés do tamanho fixo do tipo string (32)?

Acabamos de concluir que essas coisas não deveriam acontecer. Por isso, é estranho ele mostrar elas acontecendo em uma das primeiras aulas como se fosse algo normal, já que já vai confundir os alunos que tentarem reproduzir o código e criar essa barreira logo no início do curso.

solução!

Olá, Daniel.

Sobre sua última dúvida, a diferença que você percebe pode estar relacionada à implementação do compilador e da biblioteca padrão usada. No seu caso, ao compilar com g++ em um sistema de 64 bits, a Small String Optimization (SSO) pode estar fazendo com que a alocação interna da string não acione o operador new explicitamente. Isso acontece porque, para strings pequenas, o próprio objeto std::string pode armazenar os caracteres internamente sem precisar alocar memória adicional.

Quando você instancia a std::string diretamente (como no seu código com str1), o operador new não é chamado porque o compilador está usando a estratégia de alocar a memória para o objeto std::string de forma otimizada, sem precisar da alocação separada de memória. Já no caso do uso explícito de new para str2, a alocação do objeto std::string ocorre na heap, mas os caracteres da string são alocados separadamente pelo allocator. A impressão de 32 bytes no seu código ocorre porque o objeto std::string em sistemas de 64 bits geralmente tem um tamanho fixo de 32 bytes, e isso não está relacionado ao tamanho do conteúdo da string, mas ao tamanho do objeto que gerencia esse conteúdo.

Quanto ao comportamento que você viu na demonstração do Vinícius, pode ser que ele tenha feito a demonstração usando uma implementação diferente ou configurada de forma distinta. É possível que a versão do compilador ou até mesmo o ambiente de execução tenha levado a alocações diferentes.

Fico à disposição. Abraços e bons estudos!