Assim como muitas outras linguagens, o C++ tem ponteiros. Você já sabe que referências e ponteiros são parecidos, mas pense neles como um nível mais próximo do funcionamento interno do seu computador. Os ponteiros são variáveis que guardam endereços de objetos. Eles são usados para interagir diretamente com objetos, permitindo a alocação dinâmica de memória e a manipulação eficiente de dados em C++.
Se você está começando com ponteiros, eles podem parecer um pouco misteriosos, mas, depois que você se acostuma, são bem simples.
Eles são uma parte crucial do C++, então reserve um tempo para entendê-los de verdade. A versão mais básica neste conceito também é chamada de ponteiro burro ou ponteiro bruto. Com o C++ moderno, também existem os ponteiros inteligentes; o tipo básico não é nada inteligente e você precisa cuidar de todo o trabalho manualmente.
Antes de entrar nos detalhes, vale a pena entender para que servem os ponteiros. Os ponteiros são uma forma de compartilhar o endereço de um objeto com outras partes do nosso programa, o que é útil por dois motivos principais:
A declaração de um ponteiro em C++ envolve especificar o tipo de dado para o qual o ponteiro aponta, seguido de um asterisco (*) e do nome do ponteiro.
Quando um ponteiro é declarado, ele não é inicializado automaticamente.
Sem uma atribuição explícita, um ponteiro costuma guardar um valor indeterminado, muitas vezes chamado de "endereço de lixo".
Embora alguns compiladores possam inicializar ponteiros com nullptr, esse comportamento não é garantido em todos os compiladores, então é essencial não contar com ele.
A boa prática é inicializar explicitamente os ponteiros brutos e verificar se não são nulos antes de usá-los, para evitar possíveis problemas.
int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid
Para atribuir o endereço de uma variável a um ponteiro, você usa 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 precisa ser feita explicitamente, enquanto as referências funcionavam apenas como um alias.
A aritmética de ponteiros permite realizar operações aritméticas com ponteiros, o que é especialmente útil ao trabalhar com arrays. Somar um número inteiro a um ponteiro faz com que ele 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 se manifestar em resultados inesperados do programa, travamentos ou até vulnerabilidades de segurança. Um exemplo famoso das consequências do comportamento indefinido ocorreu na explosão do foguete Ariane 5 em 1996, quando uma exceção de software causada pela conversão de um número de ponto flutuante de 64 bits para um inteiro com sinal de 16 bits levou a uma falha catastrófica.
Em C++, o operador -> é usado para acessar os membros de um objeto por meio de um ponteiro para esse objeto.
É uma forma abreviada que simplifica o acesso aos membros de objetos apontados por ponteiros.
Por exemplo, se ptr é um ponteiro para um objeto com uma variável membro x, em vez de usar (*ptr).x, você pode usar diretamente ptr->x.
Esse operador melhora a legibilidade do código e reduz a verbosidade ao trabalhar com ponteiros para objetos.
Veja um exemplo breve, 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 acessar a variável membro superpower, mostrando o icônico "Laço da Verdade" 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;
Ponteiros e referências permitem o acesso indireto a objetos, mas diferem em suas capacidades e nas considerações de segurança. Os ponteiros oferecem a flexibilidade de mudar o objeto para o qual apontam e podem receber null. No entanto, essa flexibilidade traz riscos, como desreferenciar ponteiros nulos ou criar ponteiros pendentes. As referências, por outro lado, não podem ser nulas e são vinculadas a objetos válidos no momento da criação, evitando esses riscos. Como são mais seguras, as referências devem ser preferidas aos ponteiros, a menos que as funcionalidades adicionais oferecidas pelos ponteiros sejam necessárias.
Bem-vindo, Engenheiro! Você é um dos últimos veteranos da Fundação Speedywagon, uma organização secreta que, por décadas, vem combatendo ameaças antigas como os Pillar Men. No curso desse esforço, você passou anos mantendo 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 rastreiam as atividades dos Pillar Men estão com defeito. Os sistemas da Fundação são antigos, e o código interage com uma biblioteca legada de C++ que não pode ser atualizada. Sua tarefa é implementar quatro funções centrais que monitoram a atividade dos sensores de Pillar Men usando uma biblioteca antiquada baseada em ponteiros.
Como engenheiro de C++ moderno, você preferiria usar ponteiros inteligentes, mas, infelizmente, o código legado exige respeito aos velhos costumes. O destino da humanidade pode depender desses ponteiros, então prossiga com cuidado, e que a energia Hamon guie você.
Como as leituras dos sensores podem ser enormes, fornecemos uma struct simulada que é usada na biblioteca real. O código já foi implementado no arquivo de cabeçalho para você.
struct pillar_men_sensor {
int activity{};
std::string location{};
std::vector<int> data{};
};
connection_check)Sua primeira tarefa é garantir que o sensor de Pillar Men esteja conectado corretamente.
Não podemos ter alarmes falsos disparados por sensores desconectados.
Você vai escrever uma função connection_check, que testa se o ponteiro do sensor é válido verificando se ele é 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 estão à espreita nas sombras, e precisamos saber se os sensores detectaram alguma atividade.
Você vai 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 todo sensor deve disparar um alarme, a menos que haja perigo real.
A função alarm_control garante que um sensor só dispare um alarme se o nível de atividade dele for maior que 0.
Essa função também deve verificar se há sensores nulos para evitar travamentos do sistema.
pillar_men_sensor.nullptr. Se o sensor for nullptr, retorne false.true; caso contrário, retorne false.pillar_men_sensor db{9008, "songokunoie", {7, 7, 7}};
bool alarm = alarm_control(&db);
// alarm => true
uv_alarm
Nesta tarefa, você vai implementar a função uv_alarm para determinar se um alarme deve ser disparado com base nos níveis de exposição à luz UV e na atividade do sensor.
A função uv_alarm deve usar a função uv_light_heuristic fornecida, que opera sobre um vetor de dados e retorna um valor com base em certos limites.
Esta é uma versão simulada do código complexo que vai rodar em produção; por favor, não altere a interface.
Defina a função uv_alarm no namespace speedywagon. Ela 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 retornado por uv_light_heuristic for maior que o nível sensor->activity, caso contrário, retornar false.Crie sua conta no Exercism para aprender e dominar C++ com 19 conceitos100 exercícios e mentoria humana de verdade, tudo de graça.