Como muitas outras linguagens, C++ tem ponteiros. Já sabes que referências e ponteiros são semelhantes, mas pensa neles como um nível mais próximo do funcionamento interno do teu computador. Os ponteiros são variáveis que guardam endereços de objetos. São usados para interagir diretamente com objetos, permitindo alocação dinâmica de memória e manipulação eficiente de dados em C++.
Se estás a começar com ponteiros, podem parecer um pouco misteriosos, mas depois de te habituares a eles, tornam-se bastante simples.
São uma parte crucial do C++, por isso dedica algum tempo a compreendê-los realmente. A versão mais básica neste conceito também é chamada de ponteiro simples ou ponteiro bruto. Com o C++ moderno também existem ponteiros inteligentes; o tipo básico não é nada inteligente e tens de tratar de todo o trabalho manualmente.
Antes de aprofundares os detalhes, vale a pena compreender a utilização dos ponteiros. Os ponteiros são uma forma de partilhar o endereço de um objeto com outras partes do nosso programa, o que é útil por duas razões principais:
Uma declaração de ponteiro em C++ envolve especificar o tipo de dados para o qual o ponteiro aponta, seguido de um asterisco (*) e do nome do ponteiro.
Quando os ponteiros são declarados, não são inicializados automaticamente.
Sem uma atribuição explícita, um ponteiro contém normalmente um valor indeterminado, muitas vezes designado por "endereço de lixo".
Embora alguns compiladores possam inicializar ponteiros com nullptr, esse comportamento não é garantido em todos os compiladores, por isso é essencial não contar com isso.
É boa prática inicializar explicitamente os ponteiros brutos e verificar se não são nulos antes de os utilizares, para evitares potenciais problemas.
int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid
Para atribuíres o endereço de uma variável a um ponteiro, usas o operador de endereço (&).
A desreferenciação de um ponteiro é feita com o operador de indireção (*).
std::string opponent{"Solomon Lane"};
// 'ethan' points to the address of the string opponent
std::string* ethan{&opponent};
// Instead of ethan's, the opponent's name address is given to the passPort
std::string passportName{*ethan};
Atenção: a desreferenciação tem de ser feita explicitamente, ao passo que as referências funcionavam simplesmente como um alias.
A aritmética de ponteiros permite efetuar operações aritméticas com ponteiros, o que é particularmente útil quando trabalhas com arrays. Somar um número inteiro a um ponteiro faz com que aponte para um elemento diferente.
// Stargate Coordinate Code
int gateCode[] = {462, 753, 218, 611, 977};
// 'ptr' points to the first element of 'gateCode'
int* ptr{&gateCode[0]};
// Accesses the third Stargate address through pointer arithmetic
int dialedAddress{*(ptr + 2)};
// Chevron encoded! Dialing Stargate address:
openStarGate(dialedAddress);
A aritmética de ponteiros em C++ pode facilmente levar a comportamento indefinido se não for tratada com cuidado. O comportamento indefinido pode manifestar-se em resultados inesperados do programa, falhas ou até vulnerabilidades de segurança. Um exemplo infame das consequências do comportamento indefinido ocorreu na explosão do foguetão Ariane 5 em 1996, em que uma exceção de software causada pela conversão de um número de vírgula flutuante de 64 bits para um número inteiro com sinal de 16 bits levou a uma falha catastrófica.
Em C++, o operador -> é usado para aceder aos membros de um objeto através de um ponteiro para esse objeto.
É uma forma abreviada que simplifica o acesso aos membros de objetos apontados por ponteiros.
Por exemplo, se ptr for um ponteiro para um objeto com uma variável membro x, em vez de usares (*ptr).x, podes usar diretamente ptr->x.
Este operador melhora a legibilidade do código e reduz a verbosidade quando trabalhas com ponteiros para objetos.
Eis um breve exemplo, com uma struct Superhero que tem uma variável membro superpower.
A função main cria um ponteiro dianaPrince para um objeto Superhero (que representa a Mulher-Maravilha).
O operador -> é usado para aceder à variável membro superpower, mostrando o icónico "Lasso of Truth" da Mulher-Maravilha.
struct Superhero {
std::string superpower;
};
Superhero wonder_woman{};
Superhero* dianaPrince = &wonder_woman;
dianaPrince->superpower = "Lasso of Truth";
// Using the -> operator to access member variable superpower:
std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower;
Os ponteiros e as referências permitem ambos o acesso indireto a objetos, mas diferem nas suas capacidades e considerações de segurança. Os ponteiros oferecem a flexibilidade de alterar o objeto de destino e podem receber o valor nulo. No entanto, essa flexibilidade introduz riscos, como desreferenciar ponteiros nulos ou criar ponteiros pendentes. As referências, por outro lado, não podem ser nulas e ficam associadas a objetos válidos aquando da criação, evitando estes riscos. Dada a sua natureza mais segura, deve dar-se preferência às referências em vez dos ponteiros, a menos que as funcionalidades adicionais proporcionadas pelos ponteiros sejam necessárias.
Bem-vindo, Engenheiro! És um dos últimos veteranos da Fundação Speedywagon, uma organização secreta que, durante décadas, tem combatido ameaças antigas como os Pillar Men. No decorrer deste esforço, passaste anos a manter os sistemas tecnológicos da Fundação, construídos com uma mistura de tecnologia de ponta e bibliotecas antigas.
No entanto, recentemente, os sensores que monitorizam as atividades dos Pillar Men estão a funcionar mal. Os sistemas da Fundação são antigos, e o código interage com uma biblioteca C++ legada que não pode ser atualizada. A tua tarefa é implementar quatro funções principais que monitorizam a atividade dos sensores de Pillar Men usando uma biblioteca antiquada baseada em ponteiros.
Enquanto engenheiro de C++ moderno, preferirias usar ponteiros inteligentes, mas, infelizmente, o código legado exige respeito pelos velhos costumes. O destino da humanidade pode depender destes ponteiros, por isso avança com cuidado, e que a energia Hamon te guie.
Como as leituras dos sensores podem ser enormes, fornecemos uma struct de simulação que é usada na biblioteca real. O código já foi implementado para ti no ficheiro de cabeçalho.
struct pillar_men_sensor {
int activity{};
std::string location{};
std::vector<int> data{};
};
connection_check)A tua primeira tarefa é garantir que o sensor de Pillar Men está ligado corretamente.
Não podemos ter falsos alarmes desencadeados por sensores desligados.
Vais escrever uma função connection_check, que testa se o ponteiro do sensor é válido, verificando se é nullptr.
pillar_men_sensor.true se o ponteiro do sensor não for nulo e false caso contrário.pillar_men_sensor* sensor{nullptr};
bool isConnected = connection_check(sensor);
// isConnected => false
activity_counter)Os Pillar Men espreitam nas sombras e precisamos de saber se os sensores detetaram alguma atividade.
Vais escrever a função activity_counter, que recebe um array de sensores e uma capacidade que indica o número de sensores no array.
pillar_men_sensor sensor_array[3] = {{0}, {101}, {22}};
int totalActivity = activity_counter(sensor_array, 3);
// totalActivity => 123
alarm_control)Nem todos os sensores devem desencadear um alarme, a não ser que haja perigo real.
A função alarm_control garante que um sensor só desencadeia um alarme se o seu nível de atividade for superior a 0.
Esta função também deve verificar se há sensores nulos para evitar falhas no sistema.
pillar_men_sensor.nullptr. Se o sensor for nullptr, devolve false.true; caso contrário, devolve false.pillar_men_sensor db{9008, "songokunoie", {7, 7, 7}};
bool alarm = alarm_control(&db);
// alarm => true
uv_alarm
Nesta tarefa, vais implementar a função uv_alarm para determinar se um alarme deve ser desencadeado com base nos níveis de exposição à luz UV e na atividade dos sensores.
A função uv_alarm deve usar a função uv_light_heuristic fornecida, que opera sobre um vetor de dados e devolve um valor com base em determinados limiares.
Esta é uma versão de simulação do código complexo que será executado em produção; não alteres a interface, por favor.
Define a função uv_alarm no namespace speedywagon. Esta deve:
pillar_men_sensor como parâmetro.false se o ponteiro do sensor for nulo.uv_light_heuristic, passando o endereço do array data do sensor.true se o valor devolvido por uv_light_heuristic for superior ao nível de sensor->activity; caso contrário, devolver false.Inscreve-te no Exercism para aprenderes e dominares C++ com 19 conceitos100 exercícios, e mentoria humana real, tudo grátis.