C++ Structs
Até aqui, cada informação morava numa variável separada: nome, idade, nota. Isso funciona para uma aluna. Para uma turma inteira, vira uma bagunça de variáveis soltas. Uma struct junta várias informações que andam juntas num tipo novo, criado por você: o tipo Aluno, que tem nome, idade e nota. Nesta aula você vai criar a sua primeira struct, preencher, guardar várias num std::vector e passar para funções.
- O que é uma struct e como declarar uma:
struct Aluno { … };. - Como criar uma variável do tipo
Alunoe acessar os campos com o ponto:a.nome. - Por que dar valores padrão aos campos, e o que acontece quando você esquece.
- Como preencher tudo de uma vez com chaves
{}, inclusive pelo nome do campo (.nome = …, C++20). - Como guardar uma turma inteira num
std::vector<Aluno>. - Como passar uma struct para uma função sem copiar (
const Aluno&). - O que é a seta
->e qual a diferença entrestructeclass.
Todos os programas desta aula foram compilados com o g++ 15 na máquina fictícia da Ana (usuária ana, computador notebook), com o comando padrão do guia: g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa (veja Compilar com g++). Você pode repetir cada um no editor do guia.
A ideia: uma ficha de cadastro
Pense na secretaria de uma escola. Para cada aluno existe uma ficha de papel, sempre com os mesmos espaços: Nome, Idade, Nota. A ficha em branco é o modelo; cada ficha preenchida é um aluno de verdade.
A struct é o modelo da ficha. Ela diz quais espaços existem e o tipo de cada um. Depois você cria quantas fichas preenchidas quiser, todas do mesmo formato.
| Na secretaria | Em C++ | Exemplo |
|---|---|---|
| O modelo da ficha | a struct (um tipo novo) | struct Aluno { … }; |
| Cada espaço da ficha | um campo (ou membro) | std::string nome; |
| Uma ficha preenchida | uma variável do tipo Aluno | Aluno a; |
| Ler o espaço "Nome" de uma ficha | o operador ponto | a.nome |
O nome vem do inglês structure, "estrutura": uma estrutura de dados montada com peças menores.
1. Declarar a struct e criar uma variável
#include <iostream>
#include <string>
struct Aluno {
std::string nome;
int idade;
double nota;
};
int main() {
Aluno a;
a.nome = "Ana";
a.idade = 20;
a.nota = 8.5;
std::cout << a.nome << " tem " << a.idade << " anos e tirou " << a.nota << '\n';
a.nota = 9.0;
std::cout << "Nota corrigida: " << a.nota << '\n';
}
- struct Aluno
- Começa a declaração de um tipo novo chamado
Aluno. Por costume, nomes de tipos próprios começam com letra maiúscula (Aluno,Produto), e variáveis com minúscula. - { … };
- Entre as chaves ficam os campos. Repare no ponto e vírgula depois da chave: ele é obrigatório na struct (e esquecê-lo é um erro clássico, que você vai ver daqui a pouco).
- std::string nome;
- Um campo: tipo + nome, igual a declarar uma variável. Os campos podem ter tipos diferentes entre si.
- Aluno a;
- Cria uma variável chamada
ado tipoAluno. AgoraAlunofunciona comointoustd::string: é um tipo como outro qualquer. - a.nome = "Ana";
- O ponto entra na ficha
ae pega o camponome. Cada campo se comporta como uma variável comum: dá para guardar, ler e mudar.
$ g++ -std=c++23 -Wall -Wextra aluno.cpp -o aluno $ ./aluno Ana tem 20 anos e tirou 8.5 Nota corrigida: 9
O 9.0 aparece como 9 porque o cout não mostra zeros desnecessários (veja float e double).
Declare a struct fora do main, lá em cima, depois dos #include. Assim todas as funções do arquivo conhecem o tipo Aluno. Em projetos maiores, ela vai para um arquivo .h (veja Arquivos .h e .cpp).
Cada variável tem a sua própria ficha
Duas variáveis do tipo Aluno são duas fichas separadas. Copiar uma struct com = copia todos os campos, e a cópia é independente:
#include <iostream>
#include <string>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
void dar_bonus(Aluno a) {
a.nota += 1.0;
std::cout << "Dentro da função: " << a.nota << '\n';
}
int main() {
Aluno ana{"Ana", 20, 6.5};
dar_bonus(ana);
std::cout << "No main: " << ana.nota << '\n';
Aluno outra = ana;
outra.nome = "Bia";
std::cout << ana.nome << " e " << outra.nome << '\n';
}
$ g++ -std=c++23 -Wall -Wextra copia.cpp -o copia $ ./copia Dentro da função: 7.5 No main: 6.5 « a função mudou a cópia dela, não a Ana do main Ana e Bia « mudar a outra não muda a ana
Guarde esse comportamento da função dar_bonus: na seção 5 você vai ver como mudar a struct de verdade.
2. Campos sem valor: lixo da memória
O que tem dentro de a.idade se você nunca guardou nada lá? Em C++, um int ou double sem valor inicial não vale zero: vale o que estava naquele pedaço da memória antes, um "lixo" (veja Variáveis). Com campos de struct é igual. Só o std::string começa vazio sozinho.
#include <iostream>
#include <string>
struct Aluno {
std::string nome;
int idade;
double nota;
};
int main() {
Aluno a;
a.nome = "Ana";
std::cout << a.nome << ", " << a.idade << " anos, nota " << a.nota << '\n';
}
$ g++ -std=c++23 -Wall -Wextra lixo.cpp -o lixo $ ./lixo Ana, 658842208 anos, nota 6.31316e-310 $ ./lixo Ana, 1597478448 anos, nota 6.31785e-310 « outro número a cada execução
Repare: o g++ não deu nenhum aviso, nem com -Wall -Wextra. O programa compilou e mostrou números sem sentido, diferentes a cada vez. Ler um valor que nunca foi guardado é comportamento indefinido: o C++ não promete nada.
A solução: valores padrão nos campos
Desde o C++11, você pode dar um valor inicial a cada campo, direto na struct. Toda ficha nova já nasce com esses valores:
#include <iostream>
#include <string>
struct Aluno {
std::string nome = "sem nome";
int idade = 0;
double nota = 0.0;
};
int main() {
Aluno a;
std::cout << a.nome << ", " << a.idade << " anos, nota " << a.nota << '\n';
a.nome = "Ana";
std::cout << a.nome << ", " << a.idade << " anos, nota " << a.nota << '\n';
}
$ g++ -std=c++23 -Wall -Wextra padrao.cpp -o padrao $ ./padrao sem nome, 0 anos, nota 0 Ana, 0 anos, nota 0
Agora não existe mais lixo: o que você não preencher fica com o valor padrão. Dê sempre um valor padrão aos campos numéricos. Daqui em diante, todos os exemplos usam int idade = 0; e double nota = 0.0;. O nome fica sem valor padrão porque um std::string já começa vazio.
Missão: a variável a já existe. Preencha os três campos com o ponto, para a tela mostrar Bia tem 19 anos e nota 7.5. Rode primeiro sem mudar nada e veja os valores padrão.
#include <iostream>
#include <string>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
int main() {
Aluno a;
// preencha os três campos de a aqui
std::cout << a.nome << " tem " << a.idade << " anos e nota " << a.nota << '\n';
}
#include <iostream>
#include <string>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
int main() {
Aluno a;
a.nome = "Bia";
a.idade = 19;
a.nota = 7.5;
std::cout << a.nome << " tem " << a.idade << " anos e nota " << a.nota << '\n';
}
3. Preencher tudo de uma vez com chaves
Preencher campo por campo dá trabalho. Você pode criar a variável já preenchida, colocando os valores entre chaves, na ordem em que os campos foram declarados: primeiro o nome, depois a idade, depois a nota.
#include <iostream>
#include <string>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
int main() {
Aluno a{"Ana", 20, 8.5};
Aluno b{"Bruno", 19};
Aluno c{};
Aluno d{.nome = "Carla", .idade = 22, .nota = 9.5};
Aluno e{.nome = "Davi", .nota = 6.0};
std::cout << a.nome << " | " << a.idade << " | " << a.nota << '\n';
std::cout << b.nome << " | " << b.idade << " | " << b.nota << '\n';
std::cout << "[" << c.nome << "] | " << c.idade << " | " << c.nota << '\n';
std::cout << d.nome << " | " << d.idade << " | " << d.nota << '\n';
std::cout << e.nome << " | " << e.idade << " | " << e.nota << '\n';
}
$ g++ -std=c++23 -Wall -Wextra chaves.cpp -o chaves
$ ./chaves
Ana | 20 | 8.5
Bruno | 19 | 0 « faltou a nota: ficou o valor padrão
[] | 0 | 0 « {} vazio: tudo no valor padrão (o nome, texto vazio)
Carla | 22 | 9.5
Davi | 0 | 6 « pulou a idade: ficou o valor padrão
| Linha | O que faz |
|---|---|
Aluno a{"Ana", 20, 8.5}; | Preenche os campos pela ordem: nome, idade, nota. |
Aluno b{"Bruno", 19}; | Valores a menos: os campos do fim ficam com o valor padrão. |
Aluno c{}; | Chaves vazias: todos os campos com o valor padrão (ou zero, se o campo não tiver padrão). É a forma segura de criar uma ficha "em branco". |
Aluno d{.nome = "Carla", .idade = 22, .nota = 9.5}; | Inicialização designada C++20: cada valor vem com o nome do campo, com um ponto na frente. Mais fácil de ler. |
Aluno e{.nome = "Davi", .nota = 6.0}; | Na designada, dá para pular campos. Os pulados ficam com o valor padrão. |
A forma designada é ótima quando a struct tem vários campos do mesmo tipo: em {20, 22} você precisa lembrar qual número é qual; em {.idade = 20, .sala = 22}, não. Antes do C++20, ela não existia em C++ (só na linguagem C): use a forma pela ordem.
As regras das chaves
As chaves conferem o que você escreve. Três erros comuns, com as mensagens reais do g++:
Na designada, a ordem tem de ser a da declaração. Você pode pular campos, mas não pode trocar a ordem:
#include <string>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
int main() {
Aluno a{.idade = 20, .nome = "Ana"};
}
$ g++ -std=c++23 -Wall -Wextra ordem.cpp -o ordem
ordem.cpp: In function ‘int main()’:
ordem.cpp:10:39: warning: missing initializer for member ‘Aluno::nome’ [-Wmissing-field-initializers]
10 | Aluno a{.idade = 20, .nome = "Ana"};
| ^
ordem.cpp:10:39: error: designator order for field ‘Aluno::nome’ does not match declaration order in ‘Aluno’
designator order … does not match declaration order = "a ordem dos nomes não bate com a ordem da declaração". O aviso de cima é consequência: depois de .idade, o g++ esperava que o nome já tivesse ficado para trás. Conserto: Aluno a{.nome = "Ana", .idade = 20};.
O compilador do editor é o Clang, que aceita a ordem trocada e só avisa: warning: ISO C++ requires field designators to be specified in declaration order (o padrão do C++ exige a ordem da declaração). O g++ recusa. Escreva sempre na ordem e o código funciona nos dois.
Valores a mais não cabem:
$ g++ -std=c++23 -Wall -Wextra demais.cpp -o demais
demais.cpp: In function ‘int main()’:
demais.cpp:10:30: error: too many initializers for ‘Aluno’
10 | Aluno a{"Ana", 20, 8.5, 3};
| ^
too many initializers = "valores iniciais demais": o Aluno tem três campos e você passou quatro valores.
As chaves não deixam perder parte de um número:
$ g++ -std=c++23 -Wall -Wextra estreita.cpp -o estreita
estreita.cpp: In function ‘int main()’:
estreita.cpp:10:20: error: narrowing conversion of ‘2.05e+1’ from ‘double’ to ‘int’ [-Wnarrowing]
10 | Aluno a{"Ana", 20.5, 8.5};
| ^~~~
A idade é int, e 20.5 perderia o .5. Isso se chama conversão que estreita (narrowing), e com chaves ela vira erro (veja Conversão de Tipos). O 2.05e+1 é o jeito do g++ escrever 20.5 (2,05 × 10¹). É uma proteção: com a.idade = 20.5; o número seria cortado em silêncio.
4. Uma turma inteira: vetor de structs
Aqui a struct mostra a sua força. Um std::vector<Aluno> é uma lista de fichas (veja std::vector). Cada elemento é um Aluno completo:
#include <iostream>
#include <string>
#include <vector>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
int main() {
std::vector<Aluno> turma = {
{"Ana", 20, 8.5},
{"Bruno", 19, 6.0},
{"Carla", 22, 9.5},
};
turma.push_back({"Davi", 21, 7.0});
double soma = 0.0;
for (const auto& aluno : turma) {
std::cout << aluno.nome << ": " << aluno.nota << '\n';
soma += aluno.nota;
}
std::cout << "Alunos: " << turma.size() << '\n';
std::cout << "Média da turma: " << soma / turma.size() << '\n';
std::cout << "Primeiro: " << turma[0].nome << '\n';
}
- std::vector<Aluno> turma = { … };
- Um vetor de alunos. Cada
{"Ana", 20, 8.5}lá dentro vira umAluno, pelas mesmas regras da seção 3. A vírgula depois do último é permitida e facilita acrescentar linhas. - turma.push_back({"Davi", 21, 7.0});
- Acrescenta mais um aluno no fim. As chaves criam o
Alunoali mesmo. - for (const auto& aluno : turma)
- Percorre a turma (veja for de Intervalo). O
const auto&olha cada ficha sem copiar e sem poder mudá-la. - aluno.nota
- Dentro do laço,
alunoé uma ficha; o ponto pega o campo. - turma[0].nome
- Primeiro o
[0]pega a ficha da posição 0; depois o.nomepega o campo dela.
$ g++ -std=c++23 -Wall -Wextra turma.cpp -o turma $ ./turma Ana: 8.5 Bruno: 6 Carla: 9.5 Davi: 7 Alunos: 4 Média da turma: 7.75 Primeiro: Ana
Sem struct, você precisaria de três vetores separados (nomes, idades, notas) e de cuidado para que a posição 2 de cada um fosse sempre a mesma pessoa. Com a struct, cada pessoa é uma peça só, e ordenar ou apagar um aluno nunca separa o nome da nota.
Missão: acrescente a Carla (22 anos, nota 9.5) à turma com push_back, para ela aparecer na lista. Se quiser, use a forma designada dentro do push_back.
#include <iostream>
#include <string>
#include <vector>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
int main() {
std::vector<Aluno> turma = {
{"Ana", 20, 8.5},
{"Bruno", 19, 6.0},
};
// acrescente a Carla (22 anos, nota 9.5) aqui
for (const auto& aluno : turma) {
std::cout << aluno.nome << ": " << aluno.nota << '\n';
}
}
#include <iostream>
#include <string>
#include <vector>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
int main() {
std::vector<Aluno> turma = {
{"Ana", 20, 8.5},
{"Bruno", 19, 6.0},
};
turma.push_back({.nome = "Carla", .idade = 22, .nota = 9.5});
for (const auto& aluno : turma) {
std::cout << aluno.nome << ": " << aluno.nota << '\n';
}
}
5. Struct e funções
Uma struct pode ser parâmetro e retorno de função, como qualquer tipo. A questão é como passar. Você já viu as três formas em Por Valor × Por Referência; com structs, a escolha pesa mais, porque uma struct pode ser grande (um Aluno carrega um std::string inteiro).
#include <iostream>
#include <string>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
void mostrar(const Aluno& a) {
std::cout << a.nome << " (" << a.idade << " anos): " << a.nota << '\n';
}
bool aprovado(const Aluno& a) {
return a.nota >= 7.0;
}
void dar_bonus(Aluno& a, double pontos) {
a.nota += pontos;
}
Aluno criar(std::string nome) {
return {nome, 18, 0.0};
}
int main() {
Aluno ana{"Ana", 20, 6.5};
mostrar(ana);
std::cout << "Aprovada? " << (aprovado(ana) ? "sim" : "não") << '\n';
dar_bonus(ana, 1.0);
mostrar(ana);
std::cout << "Aprovada? " << (aprovado(ana) ? "sim" : "não") << '\n';
Aluno novo = criar("Eva");
mostrar(novo);
}
$ g++ -std=c++23 -Wall -Wextra funcao.cpp -o funcao $ ./funcao Ana (20 anos): 6.5 Aprovada? não Ana (20 anos): 7.5 « o bônus mudou a Ana do main Aprovada? sim Eva (18 anos): 0
| Parâmetro | O que acontece | Quando usar |
|---|---|---|
const Aluno& a | A função olha a ficha original, sem copiar, e não pode mudar nada. | O padrão para structs: mostrar, aprovado. |
Aluno& a | A função mexe na ficha original. | Quando o objetivo é mudar: dar_bonus. |
Aluno a | A função recebe uma cópia inteira; mudanças se perdem (você viu em copia.cpp). | Raramente; quando a função precisa de uma cópia para mexer à vontade. |
E o retorno? Aluno criar(…) devolve uma struct inteira. O return {nome, 18, 0.0}; monta o Aluno com chaves direto no return: o compilador sabe que o tipo é Aluno por causa da declaração da função. Devolver struct por valor é normal e barato no C++ moderno.
O const protege a ficha
Se uma função recebe const Aluno& e tenta mudar um campo, o compilador barra:
#include <string>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
void dar_bonus(const Aluno& a) {
a.nota += 1.0;
}
int main() {
Aluno ana{"Ana", 20, 6.5};
dar_bonus(ana);
}
$ g++ -std=c++23 -Wall -Wextra constmuda.cpp -o constmuda
constmuda.cpp: In function ‘void dar_bonus(const Aluno&)’:
constmuda.cpp:10:12: error: assignment of member ‘Aluno::nota’ in read-only object
10 | a.nota += 1.0;
| ~~~~~~~^~~~~~
assignment of member ‘Aluno::nota’ in read-only object = "atribuição ao campo nota de um objeto somente leitura". Aluno::nota quer dizer "o campo nota que pertence a Aluno". Se a função deve mudar a ficha, tire o const; se não deve, o erro acabou de achar um engano seu.
6. A seta ->: quando você tem um ponteiro
Se em vez da struct você tem um ponteiro para ela (veja Ponteiros), o ponto não serve direto. Você pode escrever (*p).nome, mas existe um atalho: a seta p->nome, que quer dizer exatamente a mesma coisa.
#include <iostream>
#include <string>
struct Aluno {
std::string nome;
int idade = 0;
double nota = 0.0;
};
int main() {
Aluno ana{"Ana", 20, 8.5};
Aluno* p = &ana;
std::cout << (*p).nome << '\n';
std::cout << p->nome << '\n';
p->nota = 10.0;
std::cout << ana.nota << '\n';
}
$ g++ -std=c++23 -Wall -Wextra seta.cpp -o seta $ ./seta Ana Ana 10
Regra para lembrar: variável ou referência → ponto; ponteiro → seta. Você vai encontrar a seta com ponteiros inteligentes e em código mais antigo. Para structs comuns, vetores e referências, é sempre o ponto.
7. struct × class
No Nível 3 você vai aprender classes. Spoiler: em C++, struct e class são quase a mesma coisa. As duas podem ter campos, funções dentro e construtores. A única diferença é o padrão de acesso:
- na
struct, tudo é público (public) se você não disser nada: qualquer parte do programa pode ler e mudar os campos; - na
class, tudo é privado (private) se você não disser nada: só o código de dentro da classe mexe nos campos.
#include <iostream>
struct PontoS {
int x = 0;
int y = 0;
};
class PontoC {
int x = 0;
int y = 0;
};
int main() {
PontoS s;
s.x = 3;
PontoC c;
c.x = 3;
std::cout << s.x << '\n';
}
$ g++ -std=c++23 -Wall -Wextra classe.cpp -o classe
classe.cpp: In function ‘int main()’:
classe.cpp:17:7: error: ‘int PontoC::x’ is private within this context
17 | c.x = 3;
| ^
classe.cpp:9:9: note: declared private here
9 | int x = 0;
| ^
O s.x = 3 da struct passou; o c.x = 3 da classe deu is private within this context ("é privado neste contexto"). O costume da comunidade C++: use struct para um "pacote de dados" simples, sem regras (como o Aluno); use class quando o tipo precisa proteger os seus dados (uma conta bancária que não pode ficar com saldo negativo). Isso é assunto de public e private.
Formas de criar uma struct: qual usar
| Forma | Exemplo | Quando usar |
|---|---|---|
| Campo por campo | Aluno a; a.nome = "Ana"; | Quando os valores chegam aos poucos (lidos do teclado, por exemplo). Exige valores padrão nos campos. |
| Chaves pela ordem | Aluno a{"Ana", 20, 8.5}; | Structs pequenas, com tipos diferentes, em listas (vector). |
| Designada C++20 | Aluno a{.nome = "Ana", .nota = 8.5}; | Structs com muitos campos ou campos do mesmo tipo. A mais legível. |
| Chaves vazias | Aluno a{}; | Uma ficha em branco, com tudo no padrão ou zerado, mesmo sem valores padrão na struct. |
Quando dá errado
#include <iostream>
#include <string>
struct Aluno {
std::string nome;
int idade;
double nota;
}
int main() {
Aluno a;
a.nome = "Ana";
std::cout << a.nome << '\n';
}
$ g++ -std=c++23 -Wall -Wextra semponto.cpp -o semponto
semponto.cpp:8:2: error: expected ‘;’ after struct definition
8 | }
| ^
| ;
O que aconteceu: a chave que fecha a struct precisa de ; depois. Funções não precisam (int main() { … }), structs precisam; é a diferença que mais engana.
Como resolver: o g++ até mostra onde: o ^ aponta logo depois da } e a linha de baixo sugere o ;. No editor do guia, o Clang diz expected ';' after struct.
$ g++ -std=c++23 -Wall -Wextra membro.cpp -o membro
membro.cpp: In function ‘int main()’:
membro.cpp:12:20: error: ‘struct Aluno’ has no member named ‘email’
12 | std::cout << a.email << '\n';
| ^~~~~
O que aconteceu: o Aluno não tem o campo email. Acontece por erro de digitação (a.Nome em vez de a.nome) ou porque você esqueceu de declarar o campo.
Como resolver: confira o nome exato na declaração da struct (maiúsculas contam) ou acrescente o campo lá.
cout
$ g++ -std=c++23 -Wall -Wextra imprimir.cpp -o imprimir
imprimir.cpp: In function ‘int main()’:
imprimir.cpp:12:15: error: no match for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘Aluno’)
12 | std::cout << ana << '\n';
| ~~~~~~~~~ ^~ ~~~
| | |
| | Aluno
| std::ostream {aka std::basic_ostream<char>}
imprimir.cpp:12:15: note: there are 52 candidates
«… mais de 360 linhas listando os 52 "candidatos" que não serviram …»
O que aconteceu: o cout sabe mostrar números e textos, mas não sabe mostrar um Aluno: você criou o tipo, ninguém ensinou o cout a escrevê-lo. O g++ então lista tudo o que ele tentou (378 linhas ao todo). Leia só a primeira linha de error.
Como resolver: mostre campo por campo (ana.nome, ana.nota) ou escreva uma função mostrar(const Aluno&), como na seção 5. Ensinar o << a escrever um Aluno é assunto de Sobrecarga de Operadores.
658842208 anos
O que aconteceu: o campo nunca recebeu valor e não tem valor padrão. Você leu lixo da memória (seção 2). O compilador nem sempre avisa.
Como resolver: dê valor padrão aos campos (int idade = 0;) ou crie a variável com chaves (Aluno a{};).
main nada mudou
O que aconteceu: o parâmetro é Aluno a (por valor). A função mexeu numa cópia, como em copia.cpp.
Como resolver: receba por referência: void dar_bonus(Aluno& a).
Referência rápida
| Escrita | Significado |
|---|---|
struct Aluno { std::string nome; int idade = 0; }; | Declara um tipo novo com dois campos (não esqueça o ; final). |
Aluno a; | Cria uma variável; campos com o valor padrão (ou lixo, se não houver padrão). |
Aluno a{}; | Cria com tudo no padrão ou zerado. |
Aluno a{"Ana", 20}; | Preenche pela ordem dos campos. |
Aluno a{.nome = "Ana"}; | Preenche pelo nome do campo C++20, na ordem da declaração. |
a.nome | Acessa um campo (ler ou mudar). |
p->nome | Acessa um campo por um ponteiro; o mesmo que (*p).nome. |
Aluno b = a; | Copia todos os campos; b é independente. |
std::vector<Aluno> turma; | Uma lista de structs. |
void f(const Aluno& a) | Passa sem copiar e sem deixar mudar: o padrão para structs. |
void f(Aluno& a) | Passa sem copiar, deixando a função mudar o original. |
Exercícios
1. Declarar um campo
Complete a struct Produto com um campo preco do tipo double, valendo 0.0 por padrão:
2. Acessar o campo
A variável p é um Produto. Complete para mostrar o nome dele:
3. Parâmetro sem cópia
Complete o parâmetro da função, que só mostra um Produto (sem copiar e sem poder mudá-lo):
4. Com struct Aluno { std::string nome; int idade = 0; double nota = 0.0; };, o que vale b.nota depois de Aluno b{"Bruno", 19};?
5. Qual é a única diferença entre struct e class em C++?
6. p é um ponteiro para um Aluno. Como se lê o nome?
Missão final: o estoque da lojinha
Missão: a lojinha da Ana guarda cada produto numa struct Produto. Escreva a função double valor_total(const std::vector<Produto>& estoque), que soma preco * quantidade de todos os produtos, e mostre o resultado no main: Valor do estoque: R$ 52.5.
#include <iostream>
#include <string>
#include <vector>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
};
// escreva aqui a função valor_total
int main() {
std::vector<Produto> estoque = {
{"caderno", 12.5, 2},
{"caneta", 2.5, 5},
{"borracha", 1.5, 10},
};
// mostre "Valor do estoque: R$ " seguido do resultado de valor_total(estoque)
}
#include <iostream>
#include <string>
#include <vector>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
};
double valor_total(const std::vector<Produto>& estoque) {
double total = 0.0;
for (const auto& p : estoque) {
total += p.preco * p.quantidade;
}
return total;
}
int main() {
std::vector<Produto> estoque = {
{"caderno", 12.5, 2},
{"caneta", 2.5, 5},
{"borracha", 1.5, 10},
};
std::cout << "Valor do estoque: R$ " << valor_total(estoque) << '\n';
}
Resumo
- Uma struct cria um tipo novo que agrupa vários campos:
struct Aluno { std::string nome; int idade = 0; double nota = 0.0; };. Não esqueça o;depois da chave. - Acesse cada campo com o ponto:
a.nome. Com ponteiro, use a seta:p->nome. - Campo numérico sem valor = lixo. Dê valores padrão aos campos ou crie com
{}. - Preencha de uma vez com chaves:
{"Ana", 20, 8.5}(pela ordem) ou{.nome = "Ana", .nota = 8.5}(designada, C++20, na ordem da declaração). std::vector<Aluno>guarda uma turma inteira; percorra comfor (const auto& a : turma).- Passe structs para funções por
const Aluno&(só ler) ouAluno&(mudar). Por valor, a função recebe uma cópia. structeclasssó diferem no padrão: struct é pública; class é privada.