Rodrigo Strauss :: Blog
C++11: Inicialização uniforme
Algumas mudanças no C++11 são tão grandes que têm o potencial de deixar a linguagem muito melhor, porém irreconhecível. Além do recurso de lambda (do que eu já falei) os recursos de inicialização uniforme e nova sintaxe para declaração de funções são desse tipo. Hoje vou falar sobre Uniform Initialization (ou inicialização uniforme em bom português).
O C++ tem a característica de não ser uma linguagem uniforme. Isso quer dizer que uma sequência específica de tokens pode ter significados diferentes dependendo do contexto. Isso causa diversos problemas, entre ele uma dificuldade bem maior para escrever um parser (interpretador) para a linguagem. Veja a seguinte expressão:
- MeuObjeto algumaCoisa(10, "lala");
Esse código pode significar duas coisas: uma declaração de função que retorna MeuObjeto ou a declaração de uma varíavel do tipo meu objeto que tem um construtor com dois parâmetros. Isso existe devido à algo que é chamado de Most Vexing Parser, ou Interpretador Mais Vexaminoso na língua de Camões. Nesse caso específico, essa linha será interpretada como uma declaração de função, algo bem inconviniente caso o trecho acima esteja dentro de uma função:
- class MeuObjeto
- {
- public:
- MeuObjeto(int a, string b) {}
- };
- int main()
- {
- // Parece algo normal, mas isso é um erro de compilação
- // Isso será interpretado como uma declaração de função
- MeuObjeto algumaCoisa(10, "lala");
- }
Para resolver esse problema, entre outros, foi criada a inicialização uniforme:
- int main()
- {
- // Como usamos colchetes ao invés de parenteses,
- // o compilador sabe com certeza que isso é uma chamada
- // de construtor (e não uma declaração de função)
- MeuObjeto algumaCoisa{10, "lala"};
- }
Mas como sempre, a forma anterior de fazer as coisas continua valendo. Lembrem-se: o C++ tem como premissa continuar compatível com todo código fonte que já foi escrito até hoje. Essa é a maldição e a benção do C++: a linguagem evolui e fica competitiva em relação às linguagens mais novas, mas os programadores podem continuar fazendo as coisas da forma antiga e, muitas vezes, pouco recomendáveis. Um exemplo é a parte de gerenciamento de memória. Vou repetir o que digo em quase toda palestra: se você está usando new e delete você está fazendo isso errado.
O recurso de inicialização uniforme permite que toda inicialização de instâncias de objetos e estruturas usem essa nova sintaxe:
- // Lembrando que tipos fundamentais também aceitam
- // a sintaxe de construtor
- int i {3};
- // Inclusive um construtor vazio (nesse caso i2 == 0)
- int i2 {}; // empty braces initialize the object to it's default (0)
- // a std:string é uma classe, e aceita uma string
- // literal no construtor
- std::string s {"hello"};
- MeuObjeto algumaCoisa {10, "lala"};
- MeuObjeto muitasCoisas[] = { {10, "lala"}, {20, "lili"} };
É uma pequena mudança que resolve grandes problemas.
Em 17/02/2016 06:43, por Rodrigo Strauss





New/delete é citado como má prática, o que eu concordo quando é feito de forma descontrolada. A alternativa é escolher entre o uso da stack ou smart pointers.
Não discordo das afirmações acima, porém eu não vejo mal em usar new/delete ou mesmo malloc/free dependendo do contexto. Exemplos de contextos válidos na minha opinião são dentro de objetos RAII ou PImpl por exemplo. Em ambos os contextos não há risco de leaks considerando implementações corretas.
Minha pergunta aqui: minhas afirmações estão corretas ou estou deixando passar algo?