auto em C++Em C++, a palavra-chave auto é uma funcionalidade poderosa introduzida no C++11, utilizada para declarar variáveis com um tipo de dados inferido.
O compilador deduz o tipo da variável a partir do seu inicializador, o que pode tornar o código mais legível e mais fácil de manter.
Repara no seguinte exemplo, em que auto é utilizado para declarar variáveis:
auto dragon_population{3}; // dragon_population is deduced as an integer
auto westeros{7.7777}; // westeros is deduced as a double
auto wedding_location{"The Twins"}; // wedding_location is deduced as a const char*, not std::string
Em cada caso, o tipo da variável é inferido a partir do valor com que é inicializada.
A palavra-chave auto ajuda-te a escrever código mais conciso e legível, reduzindo a verbosidade dos tipos explícitos.
const std::vector<std::string> pigeon_pie{"flour", "butter", "pigeon", "salt"};
auto purple_wedding_pie{pigeon_pie};
purple_wedding_pie.emplace_back("the strangler");
Neste ciclo, auto deduz o tipo de purple_wedding_pie como std::vector<std::string>, evitando a necessidade de voltar a especificar o tipo explicitamente.
A palavra-chave auto é compatível com várias construções de C++, o que a torna uma ferramenta versátil na programação moderna em C++.
auto& element{array[0]}; // reference to an element
const auto object{otherObject}; // const type version of otherObject's type
auto* ptr{&x}; // pointer to x with the same type as x, but as a pointer.
Em conceitos posteriores veremos frequentemente a palavra-chave auto com expressões lambda, ciclos for baseados em intervalos e iteradores.
Os ponteiros inteligentes são uma funcionalidade moderna de C++ concebida para proporcionar uma gestão automática de memória, ajudando a evitar as fugas de memória e os ponteiros pendentes habitualmente associados aos ponteiros brutos. Funcionam como invólucros em torno dos ponteiros brutos, acrescentando funcionalidades adicionais, como a libertação automática de memória quando o ponteiro já não é necessário.
Os ponteiros inteligentes são habitualmente implementados como modelos de classe na biblioteca padrão de C++.
Os dois ponteiros inteligentes mais utilizados são std::unique_ptr e std::shared_ptr.
std::unique_ptr é um ponteiro inteligente que detém o objeto em exclusivo.
Garante que, em qualquer momento, apenas um objeto std::unique_ptr detém o recurso.
Quando o std::unique_ptr detentor é destruído ou reiniciado, destrói automaticamente os objetos e liberta a sua memória.
#include <memory>
// Declaring and defining a unique pointer
auto rightful_king_of_england = std::make_unique<std::string>("Excalibur");
// Unique pointers cannot be copied or assigned
auto mordred = rightful_king_of_england; // Error: Cannot copy a unique_ptr
std::make_unique()
Ao criar um std::unique_ptr, é preferível utilizar std::make_unique() em vez de recorrer diretamente a new para alocar memória.
std::make_unique() oferece várias vantagens:
std::make_unique() garante segurança relativamente a exceções.
Se for lançada uma exceção durante a construção do objeto, a memória é libertada automaticamente, evitando fugas de memória.std::make_unique() torna o código mais claro e mais conciso.
Elimina a necessidade de especificar explicitamente o tipo a alocar, uma vez que os argumentos de modelo são deduzidos automaticamente.std::make_unique() de forma mais eficaz do que alocar memória manualmente com new, o que pode resultar num melhor desempenho.std::unique_ptr é construído manualmente.
Isso levaria a um comportamento indefinido, quando o std::unique_ptr tenta eliminá-lo no fim do seu âmbito.std::shared_ptr é um ponteiro inteligente que permite que vários objetos std::shared_ptr partilhem a posse do mesmo recurso.
Mantém o registo de quantos ponteiros partilhados estão a referenciar o recurso e liberta a memória apenas quando o último ponteiro partilhado que detém o recurso sai do âmbito ou é reiniciado.
// Declaring and defining a shared pointer to a dynamically allocated string
auto martian_congressional_republic = std::make_shared<std::string>("protomolecule");
// Creating more shared pointer that shares ownership
auto outer_planets_alliance = martian_congressional_republic;
auto united_nations = martian_congressional_republic;
No C++17 e anteriores, não há suporte direto para utilizar std::shared_ptr com arrays através de std::make_shared<T[]>.
Embora seja possível alocar arrays com std::make_shared<T[]>, criar ponteiros partilhados diretamente a partir deles pode originar comportamento indefinido, devido a diferenças na gestão de memória entre objetos individuais e arrays.
Em vez disso, considera utilizar std::vector ou funções de eliminação personalizadas para gerir arrays com ponteiros partilhados de forma eficaz.
Ao lidar com alocação de arrays e ponteiros partilhados em C++17, certifica-te sempre de que há compatibilidade com o teu compilador e com a implementação da biblioteca padrão.
std::make_shared()
Tal como std::make_unique(), std::make_shared() oferece benefícios como uma maior eficiência de memória, segurança relativamente a exceções e legibilidade.
Combina a alocação de memória do bloco de controlo e do objeto gerido numa única operação, o que aumenta a eficiência e reduz o risco de fugas de memória.
Além disso, a dedução automática dos argumentos de modelo simplifica o código e melhora a legibilidade.
Utilizar std::make_shared() promove código mais limpo, mais seguro e mais eficiente quando trabalhas com objetos std::shared_ptr em C++.
Utiliza ponteiros inteligentes por predefinição: std::unique_ptr para posse exclusiva e std::shared_ptr para posse partilhada.
Reserva os ponteiros brutos para referências sem posse ou quando interages com código legado.
Na maioria dos casos, std::unique_ptr é suficiente para a posse exclusiva, uma vez que oferece uma gestão de memória leve, sem a sobrecarga da contagem de referências.
std::shared_ptr deve ser utilizado com moderação, pois introduz sobrecarga e complexidade, a menos que seja realmente necessária uma posse partilhada.
Este exercício leva-te ao mundo de Troia. A vida do seu povo está cheia de maravilhas e magia. Muitos humanos em Troia possuem poderes, que são usados frequentemente no seu dia a dia. Os poderes servem para remodelar o mundo ou influenciar a fauna de Troia e outras pessoas. A magia também se manifesta em artefactos únicos, muito procurados por aventureiros, artesãos e sábios.
Neste exercício vais escrever código para modelar os humanos de Troia, os artefactos que possuem e as interações entre poderes.
Tens seis tarefas. A primeira está relacionada com a criação de um humano. As outras cinco são sobre lidar com poderes e artefactos.
No teu modelo de Troia, os humanos são o elemento mais importante. O teu humano modelo deve ser capaz de possuir um artefacto único. Deve também ter a capacidade de manifestar um poder. Estes poderes podem afetar outros humanos, por isso também queres modelar se um humano é influenciado por outro poder.
São-te fornecidas implementações básicas das structs artifact e power.
Implementa uma struct human (ou classe) que tenha um ponteiro inteligente para uma variável membro do tipo artifact chamada possession.
Cada artefacto só pode ser possuído por um único humano em qualquer momento.
Um human tem de ter mais duas variáveis membro.
Uma guarda o seu own_power e a outra é o poder que o influencia: o influenced_by.
Tanto o own_power como o influenced_by são ponteiros inteligentes para powers.
Cada power pode ser detido por um único humano, mas também pode influenciar outros humanos ao mesmo tempo.
Por predefinição, os humanos nascem sem qualquer artefacto, não possuem poderes nem são influenciados por nenhum.
human mindy_mccready{};
mindy_mccready.possession;
// => nullptr
mindy_mccready.own_power;
// => nullptr
mindy_mccready.influenced_by;
// => nullptr
O teu modelo é aborrecido sem a interação das suas partes. Queres criar artefactos únicos e dá-los a certos humanos.
Define a função give_new_artifact, que não devolve nada mas recebe uma referência a um human e uma string.
Com a string, deve criar um novo objeto artifact e definir o ponteiro possession do human em conformidade.
A função não deve devolver nada.
human erik_magnus_lehnsherr{};
give_new_artifact(erik_magnus_lehnsherr, "Mind shielding helmet");
erik_magnus_lehnsherr.possession->name;
// "Mind shielding helmet"
No mundo de Troia, tudo gira em torno da interação. Queres que as pessoas façam negócio trocando o que possuem.
Escreve uma função exchange_artifacts que não devolve nada mas recebe dois ponteiros inteligentes de artefactos para trocar os itens.
Lembra-te de que não podes copiar um unique_ptr.
Isto inclui a sua utilização em parâmetros de função.
Usa antes uma referência ao unique_ptr.
human uchiha{};
give_new_artifact(uchiha, "konoha headband");
human uzumaki{};
give_new_artifact(uzumaki, "forehead protector");
exchange_artifacts(uchiha.possession, uzumaki.possession);
uchiha.possession->name;
// "forehead protector"
uzumaki.possession->name;
// "konoha headband"
A característica mais emocionante de Troia são os poderes especiais que as pessoas podem exercer. Alguns conseguem fundir ferro com o pensamento, enquanto outros curam qualquer ferida instantaneamente durante a noite.
Define a função manifest_power, que não devolve nada mas recebe uma referência a um human e uma string.
Com a string, deve criar um novo objeto power e definir o ponteiro own_power do human em conformidade.
A função não deve devolver nada.
human eleven {};
manifest_power(eleven, "psychokinesis");
eleven.own_power->effect;
// "psychokinesis"
De que servem os maiores poderes, se não os podes usar? O teu modelo centra-se nos humanos, por isso queres acompanhar a influência dos poderes.
Escreve uma função void use_power que recebe duas referências a humanos.
O primeiro humano é quem lança o poder e o segundo representa o alvo.
O ponteiro influenced_by do alvo deve apontar para o poder de quem o lança.
Para simplificar, os humanos só podem ser influenciados por um único poder. Este poder mantém-se mesmo que quem o lançou já não exista.
human pamela_isley{};
manifest_power(pamela_isley, "control pheromones");
human count_vertigo{};
use_power(pamela_isley, count_vertigo);
count_vertigo.influenced_by->effect;
// "control pheromones"
Certos poderes perdem potência ou desencadeiam certos efeitos na tua simulação quando são aplicados a vários humanos. Queres acompanhar o número de pessoas ligadas a cada poder.
Define a função power_intensity, que recebe um humano e devolve a intensidade do seu poder como um int.
Se a pessoa não tiver poder, o valor devolvido deve ser 0.
Caso contrário, a intensidade deve refletir quem lança o poder e todas as pessoas atualmente influenciadas.
human jean_grey{};
manifest_power(jean_grey, "uplifting personality");
human scott{};
human logan{};
human ororo{};
use_power(jean_grey, ororo);
use_power(jean_grey, logan);
use_power(jean_grey, scott);
power_intensity(jean_grey);
// 4
Inscreve-te no Exercism para aprenderes e dominares C++ com 19 conceitos100 exercícios, e mentoria humana real, tudo grátis.