Projeto Controle de Estoque
A lojinha de papelaria da Ana cresceu, e o caderno onde ela anotava o estoque não dá mais conta: ela vende cadernos sem perceber que estão acabando e não sabe quanto dinheiro está parado na prateleira. Neste projeto você vai escrever, em sete etapas, o programa que ela precisa: guarda os produtos pelo nome, registra entradas e saídas, avisa quando algo está acabando, mostra um relatório do que vale mais e grava tudo num arquivo, para nada se perder quando o programa fecha.
- struct para descrever um produto.
- std::map: guardar e achar produtos pelo nome (
find,[], e a armadilha do[]). - std::optional: uma busca que pode não encontrar nada.
- for de intervalo com desestruturação (
auto& [nome, p], de pair e tuple). - std::sort com uma lambda que diz a ordem.
- Gravar e ler um arquivo CSV (arquivos), convertendo texto em número (stoi e stod).
- Um menu de comandos digitados pelo usuário.
Todos os programas foram compilados e executados na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/cpp/estoque, com o g++ 15.2 e o comando padrão do guia, g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque. O programa inteiro fica num arquivo só, estoque.cpp, que cresce a cada etapa.
O que vamos construir
O programa pronto, numa sessão da Ana (o que vem depois de estoque> é o que ela digitou):
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque Lojinha da Ana: 5 produtos carregados. estoque> listar borracha 60 un. R$ 1.80 caderno 40 un. R$ 12.50 caneta 90 un. R$ 2.50 cola 12 un. R$ 4.75 mochila 5 un. R$ 89.90 estoque> entrada caneta 30 Entrada registrada. estoque> saida caderno 32 ALERTA: caderno ficou com 8 (mínimo 10) Saída registrada. estoque> saida mochila 10 Quantidade inválida para mochila: pedido 10, há 5 estoque> buscar mochila mochila: 5 un. a R$ 89.90 estoque> buscar glitter Não temos glitter. estoque> alertas Repor caderno: 8 no estoque, mínimo 10 Repor cola: 12 no estoque, mínimo 15 estoque> relatorio Produto Qtd Preço Valor mochila 5 89.90 449.50 caneta 120 2.50 300.00 borracha 60 1.80 108.00 caderno 8 12.50 100.00 cola 12 4.75 57.00 Total 1014.50 estoque> ajuda Comandos: listar, entrada NOME QTD, saida NOME QTD, buscar NOME, alertas, relatorio, sair estoque> sair Estoque salvo em estoque.csv. $ cat estoque.csv borracha,1.8,60,15 caderno,12.5,8,10 caneta,2.5,120,30 cola,4.75,12,15 mochila,89.9,5,2
- O produto e o map: cadastrar e listar.
- Entrada e saída de mercadoria:
finde validação. - Buscar com std::optional.
- Alerta de estoque baixo.
- Relatório ordenado por valor:
std::sorte lambda. - Salvar e carregar o CSV.
- O menu.
$ mkdir -p cpp/estoque $ cd cpp/estoque
Etapa 1 · O produto e o map
Cada produto tem nome, preço, quantidade em estoque e um mínimo: a quantidade abaixo da qual a Ana precisa comprar mais. Quatro informações que andam juntas: uma struct.
E onde guardar os produtos? Num std::vector, achar o "caderno" exigiria olhar um por um. Num std::map (mapa, "dicionário"), cada produto fica guardado debaixo de uma chave, o nome, e o map acha a chave rapidamente. De quebra, ele mantém as chaves em ordem alfabética.
#include <format>
#include <iostream>
#include <map>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
listar(estoque);
std::cout << estoque.size() << " produtos\n";
}
| Trecho | O que faz |
|---|---|
struct Produto { … }; | O molde de um produto. Todos os campos têm valor inicial (= 0): nada fica com lixo. |
using Estoque = std::map<std::string, Produto>; | Um apelido (using): Estoque é um map cuja chave é o nome (std::string) e o valor é o Produto. |
Estoque& estoque | As funções recebem o estoque por referência: trabalham no original, sem copiar. Quando só leem, const Estoque&. |
estoque[p.nome] = p; | Guarda o produto debaixo do nome dele. Se o nome já existia, substitui. |
for (const auto& [nome, p] : estoque) | Percorre o map em ordem de chave. Cada item é um par (chave, valor); [nome, p] dá um nome a cada metade (desestruturação, C++17). |
"{:<10}{:>5} un. R$ {:>6.2f}\n" | Nome em 10 colunas à esquerda, quantidade em 5 à direita, preço em 6 com 2 casas (std::format). |
cadastrar(estoque, {"caderno", 12.5, 40, 10}); | As chaves montam um Produto na ordem dos campos: nome, preço, quantidade, mínimo. |
Estoque estoqueInicial() | Monta o estoque de exemplo. Vamos reaproveitá-la em todas as etapas. |
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque borracha 60 un. R$ 1.80 caderno 40 un. R$ 12.50 caneta 120 un. R$ 2.50 cola 12 un. R$ 4.75 mochila 5 un. R$ 89.90 5 produtos
Os produtos foram cadastrados na ordem caderno, caneta, borracha, mochila, cola, mas saíram em ordem alfabética. Quem ordenou foi o std::map.
Missão (etapa 1): a Ana começou a vender tesouras: R$ 15.90, 12 unidades, mínimo 4. Cadastre a tesoura no main, logo depois de criar o estoque. A listagem deve ganhar a linha:
tesoura 12 un. R$ 15.90 6 produtos
#include <format>
#include <iostream>
#include <map>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
listar(estoque);
std::cout << estoque.size() << " produtos\n";
}
#include <format>
#include <iostream>
#include <map>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
cadastrar(estoque, {"tesoura", 15.9, 12, 4});
listar(estoque);
std::cout << estoque.size() << " produtos\n";
}
Etapa 2 · Entrada e saída de mercadoria
Chegou mercadoria: entrada, a quantidade sobe. Vendeu: saída, a quantidade desce. As duas precisam de cuidado: o produto tem de existir, a quantidade tem de ser positiva e, na saída, não dá para tirar mais do que há. As duas funções novas (o resto do arquivo continua como na etapa 1):
bool entrada(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
if (qtd <= 0) {
std::cout << "Quantidade inválida: " << qtd << '\n';
return false;
}
it->second.quantidade += qtd;
return true;
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
return true;
}
| Trecho | O que faz |
|---|---|
auto it = estoque.find(nome); | Procura a chave. Devolve um iterador: uma "seta" apontando para o item achado. |
it == estoque.end() | Se não achou, a seta aponta para end(), "depois do último". É assim que se pergunta "não existe?". |
it->second | O item é um par: it->first é a chave (o nome), it->second é o Produto guardado no map. |
Produto& p = it->second; | Um apelido (referência) para o produto dentro do map: mexer em p é mexer no estoque. Sem o &, seria uma cópia, e a saída não mudaria nada. |
return false; | Recusa e avisa quem chamou. A mensagem com o motivo já foi mostrada. |
E um main que testa os casos bons e os ruins:
int main() {
Estoque estoque = estoqueInicial();
entrada(estoque, "caneta", 30);
saida(estoque, "caderno", 32);
saida(estoque, "mochila", 10); // só há 5
saida(estoque, "cadreno", 1); // nome digitado errado
entrada(estoque, "cola", -5);
listar(estoque);
}
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque Quantidade inválida para mochila: pedido 10, há 5 Produto não encontrado: cadreno Quantidade inválida: -5 borracha 60 un. R$ 1.80 caderno 8 un. R$ 12.50 caneta 150 un. R$ 2.50 cola 12 un. R$ 4.75 mochila 5 un. R$ 89.90
Caneta: 120 + 30 = 150. Caderno: 40 − 32 = 8. As três operações erradas foram recusadas com o motivo, e nenhuma mexeu no estoque.
[] que cria produto
Por que tanto trabalho com find, se estoque["caderno"] já dá o produto? Acrescente no main, antes do listar, uma linha com o nome digitado errado:
estoque["cadreno"].quantidade -= 1; // tirar 1 caderno... com o nome errado
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque Quantidade inválida para mochila: pedido 10, há 5 Produto não encontrado: cadreno Quantidade inválida: -5 borracha 60 un. R$ 1.80 caderno 8 un. R$ 12.50 cadreno -1 un. R$ 0.00 caneta 150 un. R$ 2.50 cola 12 un. R$ 4.75 mochila 5 un. R$ 89.90
Nenhum erro, nenhum aviso, e apareceu um produto cadreno com −1 unidade e preço zero. Quando a chave não existe, o [] do map cria um item novo, com um Produto vazio, e devolve esse item. Como evitar: para procurar, use find (ou contains, C++20, que só responde sim ou não). Use [] só quando quer mesmo criar ou substituir, como no cadastrar. Mais em std::map.
Missão (etapa 2): a função entrada está vazia (por isso os avisos de parâmetro não usado). Escreva-a como a saida, mas somando: produto inexistente mostra Produto não encontrado: NOME; quantidade zero ou negativa mostra Quantidade inválida: QTD. Na saída devem aparecer:
Quantidade inválida: -5 Produto não encontrado: tesoura … caneta 150 un. R$ 2.50
#include <format>
#include <iostream>
#include <map>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool entrada(Estoque& estoque, const std::string& nome, int qtd) {
// Missão: procure o produto com find; se não existir, mostre
// "Produto não encontrado: NOME" e devolva false. Se qtd <= 0, mostre
// "Quantidade inválida: QTD" e devolva false. Senão, some e devolva true.
return false;
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
return true;
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
entrada(estoque, "caneta", 30);
saida(estoque, "caderno", 32);
saida(estoque, "mochila", 10); // só há 5
saida(estoque, "cadreno", 1); // nome digitado errado
entrada(estoque, "cola", -5);
entrada(estoque, "tesoura", 5);
listar(estoque);
}
#include <format>
#include <iostream>
#include <map>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool entrada(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
if (qtd <= 0) {
std::cout << "Quantidade inválida: " << qtd << '\n';
return false;
}
it->second.quantidade += qtd;
return true;
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
return true;
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
entrada(estoque, "caneta", 30);
saida(estoque, "caderno", 32);
saida(estoque, "mochila", 10); // só há 5
saida(estoque, "cadreno", 1); // nome digitado errado
entrada(estoque, "cola", -5);
entrada(estoque, "tesoura", 5);
listar(estoque);
}
Etapa 3 · Buscar com std::optional
Agora a Ana quer consultar um produto: "tem mochila? quanto custa?". Uma função buscar que devolve o Produto… mas e se não existir? Devolver um produto vazio confundiria (um produto com preço zero existe ou não?). Para isso existe o std::optional C++17: uma caixinha que tem um produto ou nada.
std::optional<Produto> buscar(const Estoque& estoque, const std::string& nome) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
return std::nullopt;
}
return it->second;
}
Junto com #include <optional> no topo. E o main, com três jeitos de usar o resultado:
int main() {
Estoque estoque = estoqueInicial();
std::optional<Produto> p = buscar(estoque, "mochila");
if (p.has_value()) {
std::cout << p->nome << ": " << p->quantidade << " un. a R$ " << p->preco << '\n';
}
auto q = buscar(estoque, "glitter");
if (!q) {
std::cout << "Não temos glitter.\n";
}
if (auto r = buscar(estoque, "cola")) { // cria e testa de uma vez
std::cout << "cola: " << r->quantidade << " un.\n";
}
}
| Trecho | O que faz |
|---|---|
std::optional<Produto> | "Talvez um Produto". |
return std::nullopt; | Devolve a caixa vazia (null option, "opção nula"). |
return it->second; | Devolve a caixa com uma cópia do produto. Para ler, basta; para mudar o estoque, use entrada e saida. |
p.has_value() | Tem alguma coisa dentro? |
p->nome | Usa o produto de dentro da caixa (como num ponteiro). Só depois de conferir que ela não está vazia. |
if (!q) | Um optional vale true quando tem valor. !q: "se está vazio". |
if (auto r = buscar(…)) | Cria r e testa na mesma linha. r só existe dentro do if. É o jeito mais usado. |
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque mochila: 5 un. a R$ 89.9 Não temos glitter. cola: 12 un.
O preço saiu 89.9 porque aqui usamos cout, não std::format com .2f.
No fim do main, pegue o preço do glitter sem conferir, com .value():
std::cout << buscar(estoque, "glitter").value().preco << '\n';
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque mochila: 5 un. a R$ 89.9 Não temos glitter. cola: 12 un. terminate called after throwing an instance of 'std::bad_optional_access' what(): bad optional access Abortado (imagem do núcleo gravada) ./estoque
.value() abre a caixa e, se ela estiver vazia, lança a exceção std::bad_optional_access ("acesso ruim a optional"). Ninguém a capturou, e o programa abortou (como no projeto do banco). Pior ainda seria buscar(…)->preco ou *buscar(…) numa caixa vazia: aí não há exceção, é comportamento indefinido, e o programa pode mostrar lixo sem avisar. Como consertar: confira antes (if (auto r = …)) ou dê um valor reserva: buscar(estoque, "glitter").value_or(Produto{}).preco dá 0.
Missão (etapa 3): no main, use buscar duas vezes: mostre borracha: 60 un. e, para "glitter", que não existe, Não temos glitter.
#include <format>
#include <iostream>
#include <map>
#include <optional>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
std::optional<Produto> buscar(const Estoque& estoque, const std::string& nome) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
return std::nullopt;
}
return it->second;
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
// Missão: use buscar para mostrar "borracha: 60 un."
// e, para "glitter", que não existe, "Não temos glitter."
}
#include <format>
#include <iostream>
#include <map>
#include <optional>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
std::optional<Produto> buscar(const Estoque& estoque, const std::string& nome) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
return std::nullopt;
}
return it->second;
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
if (auto p = buscar(estoque, "borracha")) {
std::cout << p->nome << ": " << p->quantidade << " un.\n";
}
if (!buscar(estoque, "glitter")) {
std::cout << "Não temos glitter.\n";
}
}
Etapa 4 · Alerta de estoque baixo
Dois avisos: um na hora, quando uma saída deixa o produto no mínimo ou abaixo dele; e um relatório de alertas, com tudo o que precisa ser reposto. O primeiro são três linhas a mais no fim da saida:
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
O segundo é uma função nova, que percorre o estoque e conta quantos alertas deu, para dizer "nenhum" quando estiver tudo bem:
void alertas(const Estoque& estoque) {
int contador = 0;
for (const auto& [nome, p] : estoque) {
if (p.quantidade <= p.minimo) {
std::cout << std::format("Repor {}: {} no estoque, mínimo {}\n",
nome, p.quantidade, p.minimo);
++contador;
}
}
if (contador == 0) {
std::cout << "Nenhum produto abaixo do mínimo.\n";
}
}
int main() {
Estoque estoque = estoqueInicial();
saida(estoque, "caderno", 32);
saida(estoque, "caneta", 50);
std::cout << "--- alertas ---\n";
alertas(estoque);
}
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque ALERTA: caderno ficou com 8 (mínimo 10) --- alertas --- Repor caderno: 8 no estoque, mínimo 10 Repor cola: 12 no estoque, mínimo 15
A saída de 32 cadernos disparou o alerta na hora (sobraram 8, mínimo 10). A de 50 canetas não: sobraram 70, bem acima de 30. E o relatório de alertas pegou também a cola, que já começou abaixo do mínimo (12 de 15): nenhuma saída a avisou, só a varredura.
Missão (etapa 4): a função alertas está vazia. Escreva-a: um for pelo estoque e, para cada produto com quantidade menor ou igual ao mínimo, a linha Repor NOME: QTD no estoque, mínimo MIN. Se nenhum precisar, Nenhum produto abaixo do mínimo.
#include <format>
#include <iostream>
#include <map>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
void alertas(const Estoque& estoque) {
// Missão: para cada produto com quantidade <= minimo, mostre
// "Repor NOME: QTD no estoque, mínimo MIN". Se nenhum, mostre
// "Nenhum produto abaixo do mínimo."
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
saida(estoque, "caderno", 32);
saida(estoque, "caneta", 50);
std::cout << "--- alertas ---\n";
alertas(estoque);
}
#include <format>
#include <iostream>
#include <map>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
void alertas(const Estoque& estoque) {
int contador = 0;
for (const auto& [nome, p] : estoque) {
if (p.quantidade <= p.minimo) {
std::cout << std::format("Repor {}: {} no estoque, mínimo {}\n",
nome, p.quantidade, p.minimo);
++contador;
}
}
if (contador == 0) {
std::cout << "Nenhum produto abaixo do mínimo.\n";
}
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
saida(estoque, "caderno", 32);
saida(estoque, "caneta", 50);
std::cout << "--- alertas ---\n";
alertas(estoque);
}
Etapa 5 · Relatório ordenado por valor
"Onde está o meu dinheiro?" O valor em estoque de um produto é preço × quantidade. A Ana quer uma tabela do que vale mais para o que vale menos, com o total no fim. O map está em ordem de nome, e não dá para reordená-lo: a ordem do map é sempre a das chaves. Então copiamos os produtos para um std::vector e ordenamos o vetor com std::sort, dizendo o critério com uma lambda.
O Produto ganha um método que calcula o valor, e entra a função relatorio (mais #include <algorithm> e #include <vector>):
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
double valor() const { return preco * quantidade; }
};
void relatorio(const Estoque& estoque) {
std::vector<Produto> lista;
for (const auto& [nome, p] : estoque) {
lista.push_back(p);
}
std::sort(lista.begin(), lista.end(), [](const Produto& a, const Produto& b) {
return a.valor() > b.valor();
});
std::cout << std::format("{:<10}{:>5}{:>9}{:>10}\n", "Produto", "Qtd", "Preço", "Valor");
double total = 0.0;
for (const auto& p : lista) {
std::cout << std::format("{:<10}{:>5}{:>9.2f}{:>10.2f}\n",
p.nome, p.quantidade, p.preco, p.valor());
total += p.valor();
}
std::cout << std::format("{:<24}{:>10.2f}\n", "Total", total);
}
| Trecho | O que faz |
|---|---|
double valor() const | Um método dentro da struct: calcula na hora, então nunca fica desatualizado. |
lista.push_back(p); | Copia cada produto do map para o vetor. |
std::sort(lista.begin(), lista.end(), …) | Ordena o vetor inteiro, do começo (begin) ao fim (end). |
[](const Produto& a, const Produto& b) { … } | A lambda: uma função sem nome, escrita ali mesmo. O sort a chama para comparar dois produtos. |
return a.valor() > b.valor(); | "a vem antes de b se vale mais." Com >, do maior para o menor; com <, do menor para o maior. |
"{:<24}{:>10.2f}\n" | A linha do total: 24 colunas para "Total" (10 + 5 + 9, as três primeiras colunas juntas) e o valor alinhado embaixo da coluna "Valor". |
int main() {
Estoque estoque = estoqueInicial();
saida(estoque, "caderno", 32);
relatorio(estoque);
}
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque ALERTA: caderno ficou com 8 (mínimo 10) Produto Qtd Preço Valor mochila 5 89.90 449.50 caneta 120 2.50 300.00 borracha 60 1.80 108.00 caderno 8 12.50 100.00 cola 12 4.75 57.00 Total 1014.50
Surpresa para a Ana: as 5 mochilas valem mais que as 120 canetas. Repare também na coluna "Preço", com acento, alinhada certinho: o std::format conta colunas, não bytes (Acentos e UTF-8).
std::ranges::sort
Desde o C++20, dá para escrever std::ranges::sort(lista, comparador), sem begin e end; ou ainda std::ranges::sort(lista, std::greater{}, &Produto::valor), que diz "ordene pelo valor, do maior para o menor". Veja Ranges.
Missão (etapa 5): a Ana quer outra visão: ordene o relatório pela quantidade, da maior para a menor. Só a lambda muda. A primeira linha depois do cabeçalho deve ser a caneta:
Produto Qtd Preço Valor caneta 120 2.50 300.00
#include <algorithm>
#include <format>
#include <iostream>
#include <map>
#include <string>
#include <vector>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
double valor() const { return preco * quantidade; }
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
void relatorio(const Estoque& estoque) {
std::vector<Produto> lista;
for (const auto& [nome, p] : estoque) {
lista.push_back(p);
}
std::sort(lista.begin(), lista.end(), [](const Produto& a, const Produto& b) {
return a.valor() > b.valor();
});
std::cout << std::format("{:<10}{:>5}{:>9}{:>10}\n", "Produto", "Qtd", "Preço", "Valor");
double total = 0.0;
for (const auto& p : lista) {
std::cout << std::format("{:<10}{:>5}{:>9.2f}{:>10.2f}\n",
p.nome, p.quantidade, p.preco, p.valor());
total += p.valor();
}
std::cout << std::format("{:<24}{:>10.2f}\n", "Total", total);
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
saida(estoque, "caderno", 32);
relatorio(estoque);
}
#include <algorithm>
#include <format>
#include <iostream>
#include <map>
#include <string>
#include <vector>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
double valor() const { return preco * quantidade; }
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
void relatorio(const Estoque& estoque) {
std::vector<Produto> lista;
for (const auto& [nome, p] : estoque) {
lista.push_back(p);
}
std::sort(lista.begin(), lista.end(), [](const Produto& a, const Produto& b) {
return a.quantidade > b.quantidade;
});
std::cout << std::format("{:<10}{:>5}{:>9}{:>10}\n", "Produto", "Qtd", "Preço", "Valor");
double total = 0.0;
for (const auto& p : lista) {
std::cout << std::format("{:<10}{:>5}{:>9.2f}{:>10.2f}\n",
p.nome, p.quantidade, p.preco, p.valor());
total += p.valor();
}
std::cout << std::format("{:<24}{:>10.2f}\n", "Total", total);
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = estoqueInicial();
saida(estoque, "caderno", 32);
relatorio(estoque);
}
Etapa 6 · Salvar e carregar o CSV
Até agora, cada vez que o programa fecha, o estoque volta ao inicial. Vamos gravar o estoque num arquivo CSV (comma-separated values, "valores separados por vírgula"): uma linha por produto, com os campos separados por vírgula. É um formato de texto simples, que qualquer planilha abre.
borracha,1.8,60,15 caderno,12.5,40,10 caneta,2.5,100,30
As duas funções novas (mais #include <fstream> e #include <sstream>):
void salvar(const Estoque& estoque, const std::string& arquivo) {
std::ofstream arq{arquivo};
for (const auto& [nome, p] : estoque) {
arq << nome << ',' << p.preco << ',' << p.quantidade << ',' << p.minimo << '\n';
}
}
Estoque carregar(const std::string& arquivo) {
Estoque estoque;
std::ifstream arq{arquivo};
std::string linha;
while (std::getline(arq, linha)) {
std::istringstream campos{linha};
std::string nome, preco, qtd, minimo;
std::getline(campos, nome, ',');
std::getline(campos, preco, ',');
std::getline(campos, qtd, ',');
std::getline(campos, minimo);
cadastrar(estoque, {nome, std::stod(preco), std::stoi(qtd), std::stoi(minimo)});
}
return estoque;
}
| Trecho | O que faz |
|---|---|
std::ofstream arq{arquivo}; | Abre o arquivo para escrever (output file stream), apagando o conteúdo antigo. Escreve-se com <<, como no cout. O arquivo é fechado sozinho no fim da função (RAII). |
std::ifstream arq{arquivo}; | Abre para ler (input file stream). Se o arquivo não existe, a leitura simplesmente não acha nada. |
while (std::getline(arq, linha)) | Lê linha por linha até o fim (getline). |
std::istringstream campos{linha}; | Trata a linha como um arquivinho, para ler pedaço por pedaço. |
std::getline(campos, nome, ','); | O terceiro argumento troca o separador: lê até a próxima vírgula. O último campo vai até o fim da linha. |
std::stod(preco), std::stoi(qtd) | Texto → número: stod = string to double, stoi = string to int (Texto ↔ Número). |
int main() {
Estoque estoque = carregar("estoque.csv");
if (estoque.empty()) {
std::cout << "Sem estoque.csv: usando o estoque inicial.\n";
estoque = estoqueInicial();
}
saida(estoque, "caneta", 10);
listar(estoque);
salvar(estoque, "estoque.csv");
}
Na primeira execução, o arquivo não existe; na segunda, o programa continua de onde parou:
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque Sem estoque.csv: usando o estoque inicial. borracha 60 un. R$ 1.80 caderno 40 un. R$ 12.50 caneta 110 un. R$ 2.50 cola 12 un. R$ 4.75 mochila 5 un. R$ 89.90 $ ./estoque borracha 60 un. R$ 1.80 caderno 40 un. R$ 12.50 caneta 100 un. R$ 2.50 cola 12 un. R$ 4.75 mochila 5 un. R$ 89.90 $ cat estoque.csv borracha,1.8,60,15 caderno,12.5,40,10 caneta,2.5,100,30 cola,4.75,12,15 mochila,89.9,5,2
A primeira execução tirou 10 canetas (120 → 110) e salvou; a segunda carregou 110 do arquivo e tirou mais 10 (→ 100). O cat mostra o arquivo de texto que o programa escreveu.
Arquivos de texto podem ser editados à mão, e gente erra. A Ana acrescentou a tesoura escrevendo a quantidade por extenso:
$ echo 'tesoura,15.9,doze,4' >> estoque.csv $ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque terminate called after throwing an instance of 'std::invalid_argument' what(): stoi Abortado (imagem do núcleo gravada) ./estoque
std::stoi("doze") não consegue converter e lança std::invalid_argument, cuja mensagem é só o nome da função, stoi. Ninguém capturou, e o programa morreu antes de abrir: uma linha ruim impede a Ana de trabalhar. Como consertar: capturar a exceção por linha, avisar qual linha foi ignorada e seguir com as outras. É a versão final do carregar:
Estoque carregar(const std::string& arquivo) {
Estoque estoque;
std::ifstream arq{arquivo};
std::string linha;
int numero = 0;
while (std::getline(arq, linha)) {
++numero;
std::istringstream campos{linha};
std::string nome, preco, qtd, minimo;
std::getline(campos, nome, ',');
std::getline(campos, preco, ',');
std::getline(campos, qtd, ',');
std::getline(campos, minimo);
try {
cadastrar(estoque, {nome, std::stod(preco), std::stoi(qtd), std::stoi(minimo)});
} catch (const std::exception&) {
std::cout << "Linha " << numero << " ignorada: " << linha << '\n';
}
}
return estoque;
}
$ g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque $ ./estoque Linha 6 ignorada: tesoura,15.9,doze,4 borracha 60 un. R$ 1.80 caderno 40 un. R$ 12.50 caneta 90 un. R$ 2.50 cola 12 un. R$ 4.75 mochila 5 un. R$ 89.90
Agora o programa abre, avisa o problema e mostra a linha inteira para a Ana corrigir. Atenção: como o salvar regrava o arquivo só com os produtos carregados, a linha ruim some do estoque.csv ao salvar. Num programa de verdade, valeria guardar uma cópia do arquivo antigo antes.
Missão (etapa 6): esta caixa tem duas abas: o programa e o estoque.csv, com a linha estragada. Rodando como está, o programa aborta. Mude o carregar para contar as linhas e capturar a exceção de cada uma, mostrando Linha 6 ignorada: tesoura,15.9,doze,4 e seguindo em frente. Depois, experimente consertar a linha na aba estoque.csv.
#include <format>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
double valor() const { return preco * quantidade; }
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
void salvar(const Estoque& estoque, const std::string& arquivo) {
std::ofstream arq{arquivo};
for (const auto& [nome, p] : estoque) {
arq << nome << ',' << p.preco << ',' << p.quantidade << ',' << p.minimo << '\n';
}
}
Estoque carregar(const std::string& arquivo) {
Estoque estoque;
std::ifstream arq{arquivo};
std::string linha;
while (std::getline(arq, linha)) {
std::istringstream campos{linha};
std::string nome, preco, qtd, minimo;
std::getline(campos, nome, ',');
std::getline(campos, preco, ',');
std::getline(campos, qtd, ',');
std::getline(campos, minimo);
cadastrar(estoque, {nome, std::stod(preco), std::stoi(qtd), std::stoi(minimo)});
}
return estoque;
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = carregar("estoque.csv");
if (estoque.empty()) {
std::cout << "Sem estoque.csv: usando o estoque inicial.\n";
estoque = estoqueInicial();
}
saida(estoque, "caneta", 10);
listar(estoque);
salvar(estoque, "estoque.csv");
}
borracha,1.8,60,15 caderno,12.5,40,10 caneta,2.5,100,30 cola,4.75,12,15 mochila,89.9,5,2 tesoura,15.9,doze,4
#include <format>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
double valor() const { return preco * quantidade; }
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
void salvar(const Estoque& estoque, const std::string& arquivo) {
std::ofstream arq{arquivo};
for (const auto& [nome, p] : estoque) {
arq << nome << ',' << p.preco << ',' << p.quantidade << ',' << p.minimo << '\n';
}
}
Estoque carregar(const std::string& arquivo) {
Estoque estoque;
std::ifstream arq{arquivo};
std::string linha;
int numero = 0;
while (std::getline(arq, linha)) {
++numero;
std::istringstream campos{linha};
std::string nome, preco, qtd, minimo;
std::getline(campos, nome, ',');
std::getline(campos, preco, ',');
std::getline(campos, qtd, ',');
std::getline(campos, minimo);
try {
cadastrar(estoque, {nome, std::stod(preco), std::stoi(qtd), std::stoi(minimo)});
} catch (const std::exception&) {
std::cout << "Linha " << numero << " ignorada: " << linha << '\n';
}
}
return estoque;
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = carregar("estoque.csv");
if (estoque.empty()) {
std::cout << "Sem estoque.csv: usando o estoque inicial.\n";
estoque = estoqueInicial();
}
saida(estoque, "caneta", 10);
listar(estoque);
salvar(estoque, "estoque.csv");
}
Etapa 7 · O menu
Falta a Ana poder usar tudo isso sem mexer no código. O main final carrega o arquivo, lê comandos até a Ana digitar sair e salva no fim. As outras funções não mudam.
int main() {
Estoque estoque = carregar("estoque.csv");
if (estoque.empty()) {
estoque = estoqueInicial();
}
std::cout << "Lojinha da Ana: " << estoque.size() << " produtos carregados.\n";
std::string linha;
while (std::cout << "estoque> " && std::getline(std::cin, linha)) {
std::istringstream pedaco{linha};
std::string comando, nome;
int qtd = 0;
pedaco >> comando >> nome >> qtd;
if (comando == "sair") {
break;
} else if (comando == "listar") {
listar(estoque);
} else if (comando == "entrada") {
if (entrada(estoque, nome, qtd)) {
std::cout << "Entrada registrada.\n";
}
} else if (comando == "saida") {
if (saida(estoque, nome, qtd)) {
std::cout << "Saída registrada.\n";
}
} else if (comando == "buscar") {
if (auto p = buscar(estoque, nome)) {
std::cout << std::format("{}: {} un. a R$ {:.2f}\n", p->nome, p->quantidade, p->preco);
} else {
std::cout << "Não temos " << nome << ".\n";
}
} else if (comando == "alertas") {
alertas(estoque);
} else if (comando == "relatorio") {
relatorio(estoque);
} else {
std::cout << "Comandos: listar, entrada NOME QTD, saida NOME QTD, buscar NOME,"
" alertas, relatorio, sair\n";
}
}
salvar(estoque, "estoque.csv");
std::cout << "\nEstoque salvo em estoque.csv.\n";
}
| Trecho | O que faz |
|---|---|
while (std::cout << "estoque> " && std::getline(std::cin, linha)) | Mostra o convite e lê uma linha inteira, até a entrada acabar. |
pedaco >> comando >> nome >> qtd; | Separa saida caderno 32 em três partes. Se faltarem partes (listar), elas ficam com o valor inicial ("" e 0). Por isso os nomes de produto não podem ter espaços. |
comando == "sair" | std::string se compara com == diretamente. |
break; | Sai do laço; o programa segue para o salvar. |
else { … Comandos: … } | Qualquer coisa desconhecida (até uma linha vazia) mostra a ajuda. |
"…, buscar NOME," " alertas, …" | Dois textos lado a lado viram um só: o jeito de quebrar uma frase comprida em duas linhas de código. |
A sessão completa está no começo da página, em O que vamos construir. Se a Ana apertar Ctrl+D (o "fim da entrada" no terminal) em vez de digitar sair, o getline falha, o laço termina e o estoque é salvo do mesmo jeito.
Missão (etapa 7): crie o comando valor NOME, que mostra o valor em estoque de um produto (Valor em estoque de mochila: R$ 449.50) ou Não temos NOME.. Use buscar e o método valor(). A caixa já digita valor mochila, valor caneta, valor glitter e sair.
#include <algorithm>
#include <format>
#include <fstream>
#include <iostream>
#include <map>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
double valor() const { return preco * quantidade; }
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool entrada(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
if (qtd <= 0) {
std::cout << "Quantidade inválida: " << qtd << '\n';
return false;
}
it->second.quantidade += qtd;
return true;
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
std::optional<Produto> buscar(const Estoque& estoque, const std::string& nome) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
return std::nullopt;
}
return it->second;
}
void alertas(const Estoque& estoque) {
int contador = 0;
for (const auto& [nome, p] : estoque) {
if (p.quantidade <= p.minimo) {
std::cout << std::format("Repor {}: {} no estoque, mínimo {}\n",
nome, p.quantidade, p.minimo);
++contador;
}
}
if (contador == 0) {
std::cout << "Nenhum produto abaixo do mínimo.\n";
}
}
void relatorio(const Estoque& estoque) {
std::vector<Produto> lista;
for (const auto& [nome, p] : estoque) {
lista.push_back(p);
}
std::sort(lista.begin(), lista.end(), [](const Produto& a, const Produto& b) {
return a.valor() > b.valor();
});
std::cout << std::format("{:<10}{:>5}{:>9}{:>10}\n", "Produto", "Qtd", "Preço", "Valor");
double total = 0.0;
for (const auto& p : lista) {
std::cout << std::format("{:<10}{:>5}{:>9.2f}{:>10.2f}\n",
p.nome, p.quantidade, p.preco, p.valor());
total += p.valor();
}
std::cout << std::format("{:<24}{:>10.2f}\n", "Total", total);
}
void salvar(const Estoque& estoque, const std::string& arquivo) {
std::ofstream arq{arquivo};
for (const auto& [nome, p] : estoque) {
arq << nome << ',' << p.preco << ',' << p.quantidade << ',' << p.minimo << '\n';
}
}
Estoque carregar(const std::string& arquivo) {
Estoque estoque;
std::ifstream arq{arquivo};
std::string linha;
int numero = 0;
while (std::getline(arq, linha)) {
++numero;
std::istringstream campos{linha};
std::string nome, preco, qtd, minimo;
std::getline(campos, nome, ',');
std::getline(campos, preco, ',');
std::getline(campos, qtd, ',');
std::getline(campos, minimo);
try {
cadastrar(estoque, {nome, std::stod(preco), std::stoi(qtd), std::stoi(minimo)});
} catch (const std::exception&) {
std::cout << "Linha " << numero << " ignorada: " << linha << '\n';
}
}
return estoque;
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = carregar("estoque.csv");
if (estoque.empty()) {
estoque = estoqueInicial();
}
std::cout << "Lojinha da Ana: " << estoque.size() << " produtos carregados.\n";
std::string linha;
while (std::cout << "estoque> " && std::getline(std::cin, linha)) {
std::istringstream pedaco{linha};
std::string comando, nome;
int qtd = 0;
pedaco >> comando >> nome >> qtd;
if (comando == "sair") {
break;
} else if (comando == "listar") {
listar(estoque);
} else if (comando == "entrada") {
if (entrada(estoque, nome, qtd)) {
std::cout << "Entrada registrada.\n";
}
} else if (comando == "saida") {
if (saida(estoque, nome, qtd)) {
std::cout << "Saída registrada.\n";
}
} else if (comando == "buscar") {
if (auto p = buscar(estoque, nome)) {
std::cout << std::format("{}: {} un. a R$ {:.2f}\n", p->nome, p->quantidade, p->preco);
} else {
std::cout << "Não temos " << nome << ".\n";
}
} else if (comando == "alertas") {
alertas(estoque);
} else if (comando == "relatorio") {
relatorio(estoque);
} else {
std::cout << "Comandos: listar, entrada NOME QTD, saida NOME QTD, buscar NOME,"
" alertas, relatorio, sair\n";
}
}
salvar(estoque, "estoque.csv");
std::cout << "\nEstoque salvo em estoque.csv.\n";
}
borracha,1.8,60,15 caderno,12.5,40,10 caneta,2.5,100,30 cola,4.75,12,15 mochila,89.9,5,2
#include <algorithm>
#include <format>
#include <fstream>
#include <iostream>
#include <map>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
double valor() const { return preco * quantidade; }
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool entrada(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
if (qtd <= 0) {
std::cout << "Quantidade inválida: " << qtd << '\n';
return false;
}
it->second.quantidade += qtd;
return true;
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
std::optional<Produto> buscar(const Estoque& estoque, const std::string& nome) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
return std::nullopt;
}
return it->second;
}
void alertas(const Estoque& estoque) {
int contador = 0;
for (const auto& [nome, p] : estoque) {
if (p.quantidade <= p.minimo) {
std::cout << std::format("Repor {}: {} no estoque, mínimo {}\n",
nome, p.quantidade, p.minimo);
++contador;
}
}
if (contador == 0) {
std::cout << "Nenhum produto abaixo do mínimo.\n";
}
}
void relatorio(const Estoque& estoque) {
std::vector<Produto> lista;
for (const auto& [nome, p] : estoque) {
lista.push_back(p);
}
std::sort(lista.begin(), lista.end(), [](const Produto& a, const Produto& b) {
return a.valor() > b.valor();
});
std::cout << std::format("{:<10}{:>5}{:>9}{:>10}\n", "Produto", "Qtd", "Preço", "Valor");
double total = 0.0;
for (const auto& p : lista) {
std::cout << std::format("{:<10}{:>5}{:>9.2f}{:>10.2f}\n",
p.nome, p.quantidade, p.preco, p.valor());
total += p.valor();
}
std::cout << std::format("{:<24}{:>10.2f}\n", "Total", total);
}
void salvar(const Estoque& estoque, const std::string& arquivo) {
std::ofstream arq{arquivo};
for (const auto& [nome, p] : estoque) {
arq << nome << ',' << p.preco << ',' << p.quantidade << ',' << p.minimo << '\n';
}
}
Estoque carregar(const std::string& arquivo) {
Estoque estoque;
std::ifstream arq{arquivo};
std::string linha;
int numero = 0;
while (std::getline(arq, linha)) {
++numero;
std::istringstream campos{linha};
std::string nome, preco, qtd, minimo;
std::getline(campos, nome, ',');
std::getline(campos, preco, ',');
std::getline(campos, qtd, ',');
std::getline(campos, minimo);
try {
cadastrar(estoque, {nome, std::stod(preco), std::stoi(qtd), std::stoi(minimo)});
} catch (const std::exception&) {
std::cout << "Linha " << numero << " ignorada: " << linha << '\n';
}
}
return estoque;
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = carregar("estoque.csv");
if (estoque.empty()) {
estoque = estoqueInicial();
}
std::cout << "Lojinha da Ana: " << estoque.size() << " produtos carregados.\n";
std::string linha;
while (std::cout << "estoque> " && std::getline(std::cin, linha)) {
std::istringstream pedaco{linha};
std::string comando, nome;
int qtd = 0;
pedaco >> comando >> nome >> qtd;
if (comando == "sair") {
break;
} else if (comando == "listar") {
listar(estoque);
} else if (comando == "entrada") {
if (entrada(estoque, nome, qtd)) {
std::cout << "Entrada registrada.\n";
}
} else if (comando == "saida") {
if (saida(estoque, nome, qtd)) {
std::cout << "Saída registrada.\n";
}
} else if (comando == "buscar") {
if (auto p = buscar(estoque, nome)) {
std::cout << std::format("{}: {} un. a R$ {:.2f}\n", p->nome, p->quantidade, p->preco);
} else {
std::cout << "Não temos " << nome << ".\n";
}
} else if (comando == "valor") {
if (auto p = buscar(estoque, nome)) {
std::cout << std::format("Valor em estoque de {}: R$ {:.2f}\n", p->nome, p->valor());
} else {
std::cout << "Não temos " << nome << ".\n";
}
} else if (comando == "alertas") {
alertas(estoque);
} else if (comando == "relatorio") {
relatorio(estoque);
} else {
std::cout << "Comandos: listar, entrada NOME QTD, saida NOME QTD, buscar NOME,"
" alertas, relatorio, sair\n";
}
}
salvar(estoque, "estoque.csv");
std::cout << "\nEstoque salvo em estoque.csv.\n";
}
O código completo
O estoque.cpp final. Compile com g++ -std=c++23 -Wall -Wextra estoque.cpp -o estoque; na primeira vez, sem estoque.csv, ele começa com o estoque de exemplo.
#include <algorithm>
#include <format>
#include <fstream>
#include <iostream>
#include <map>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
struct Produto {
std::string nome;
double preco = 0.0;
int quantidade = 0;
int minimo = 0; // abaixo disso, repor
double valor() const { return preco * quantidade; }
};
using Estoque = std::map<std::string, Produto>;
void cadastrar(Estoque& estoque, const Produto& p) {
estoque[p.nome] = p;
}
void listar(const Estoque& estoque) {
for (const auto& [nome, p] : estoque) {
std::cout << std::format("{:<10}{:>5} un. R$ {:>6.2f}\n", nome, p.quantidade, p.preco);
}
}
bool entrada(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
if (qtd <= 0) {
std::cout << "Quantidade inválida: " << qtd << '\n';
return false;
}
it->second.quantidade += qtd;
return true;
}
bool saida(Estoque& estoque, const std::string& nome, int qtd) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
std::cout << "Produto não encontrado: " << nome << '\n';
return false;
}
Produto& p = it->second;
if (qtd <= 0 || qtd > p.quantidade) {
std::cout << std::format("Quantidade inválida para {}: pedido {}, há {}\n",
nome, qtd, p.quantidade);
return false;
}
p.quantidade -= qtd;
if (p.quantidade <= p.minimo) {
std::cout << std::format("ALERTA: {} ficou com {} (mínimo {})\n",
nome, p.quantidade, p.minimo);
}
return true;
}
std::optional<Produto> buscar(const Estoque& estoque, const std::string& nome) {
auto it = estoque.find(nome);
if (it == estoque.end()) {
return std::nullopt;
}
return it->second;
}
void alertas(const Estoque& estoque) {
int contador = 0;
for (const auto& [nome, p] : estoque) {
if (p.quantidade <= p.minimo) {
std::cout << std::format("Repor {}: {} no estoque, mínimo {}\n",
nome, p.quantidade, p.minimo);
++contador;
}
}
if (contador == 0) {
std::cout << "Nenhum produto abaixo do mínimo.\n";
}
}
void relatorio(const Estoque& estoque) {
std::vector<Produto> lista;
for (const auto& [nome, p] : estoque) {
lista.push_back(p);
}
std::sort(lista.begin(), lista.end(), [](const Produto& a, const Produto& b) {
return a.valor() > b.valor();
});
std::cout << std::format("{:<10}{:>5}{:>9}{:>10}\n", "Produto", "Qtd", "Preço", "Valor");
double total = 0.0;
for (const auto& p : lista) {
std::cout << std::format("{:<10}{:>5}{:>9.2f}{:>10.2f}\n",
p.nome, p.quantidade, p.preco, p.valor());
total += p.valor();
}
std::cout << std::format("{:<24}{:>10.2f}\n", "Total", total);
}
void salvar(const Estoque& estoque, const std::string& arquivo) {
std::ofstream arq{arquivo};
for (const auto& [nome, p] : estoque) {
arq << nome << ',' << p.preco << ',' << p.quantidade << ',' << p.minimo << '\n';
}
}
Estoque carregar(const std::string& arquivo) {
Estoque estoque;
std::ifstream arq{arquivo};
std::string linha;
int numero = 0;
while (std::getline(arq, linha)) {
++numero;
std::istringstream campos{linha};
std::string nome, preco, qtd, minimo;
std::getline(campos, nome, ',');
std::getline(campos, preco, ',');
std::getline(campos, qtd, ',');
std::getline(campos, minimo);
try {
cadastrar(estoque, {nome, std::stod(preco), std::stoi(qtd), std::stoi(minimo)});
} catch (const std::exception&) {
std::cout << "Linha " << numero << " ignorada: " << linha << '\n';
}
}
return estoque;
}
Estoque estoqueInicial() {
Estoque estoque;
cadastrar(estoque, {"caderno", 12.5, 40, 10});
cadastrar(estoque, {"caneta", 2.5, 120, 30});
cadastrar(estoque, {"borracha", 1.8, 60, 15});
cadastrar(estoque, {"mochila", 89.9, 5, 2});
cadastrar(estoque, {"cola", 4.75, 12, 15});
return estoque;
}
int main() {
Estoque estoque = carregar("estoque.csv");
if (estoque.empty()) {
estoque = estoqueInicial();
}
std::cout << "Lojinha da Ana: " << estoque.size() << " produtos carregados.\n";
std::string linha;
while (std::cout << "estoque> " && std::getline(std::cin, linha)) {
std::istringstream pedaco{linha};
std::string comando, nome;
int qtd = 0;
pedaco >> comando >> nome >> qtd;
if (comando == "sair") {
break;
} else if (comando == "listar") {
listar(estoque);
} else if (comando == "entrada") {
if (entrada(estoque, nome, qtd)) {
std::cout << "Entrada registrada.\n";
}
} else if (comando == "saida") {
if (saida(estoque, nome, qtd)) {
std::cout << "Saída registrada.\n";
}
} else if (comando == "buscar") {
if (auto p = buscar(estoque, nome)) {
std::cout << std::format("{}: {} un. a R$ {:.2f}\n", p->nome, p->quantidade, p->preco);
} else {
std::cout << "Não temos " << nome << ".\n";
}
} else if (comando == "alertas") {
alertas(estoque);
} else if (comando == "relatorio") {
relatorio(estoque);
} else {
std::cout << "Comandos: listar, entrada NOME QTD, saida NOME QTD, buscar NOME,"
" alertas, relatorio, sair\n";
}
}
salvar(estoque, "estoque.csv");
std::cout << "\nEstoque salvo em estoque.csv.\n";
}
Desafios extras
- Cadastrar pelo menu. Comando
novo NOME PRECO QTD MIN. Recuse se o produto já existir (contains, C++20). - Remover. Comando
remover NOMEcomestoque.erase(nome). Oerasedevolve quantos itens apagou (0 ou 1): use para avisar "não existe". - Nomes com espaço. "caneta azul" quebra o menu. Troque o separador do menu, por exemplo
saida;caneta azul;3, comgetline(pedaco, nome, ';'). - Histórico. Grave cada entrada e saída num segundo arquivo,
movimentos.csv, aberto comstd::ofstream arq{"movimentos.csv", std::ios::app}(app= acrescentar no fim, sem apagar). - Cópia de segurança. Antes de salvar, renomeie o arquivo antigo para
estoque.csv.bakcomstd::filesystem::rename. - Busca por pedaço.
buscar cadlista todos os produtos cujo nome contém "cad" (nome.find(trecho) != std::string::npos, veja a referência de std::string).
Resumo
std::map<std::string, Produto>guarda pela chave e percorre em ordem alfabética;for (const auto& [nome, p] : estoque)separa chave e valor.- Para procurar,
finde compare comend(). O[]cria a chave que não existe: um erro de digitação vira produto novo. Produto& p = it->second;mexe no produto dentro do map; sem&, numa cópia.std::optional<Produto>= "um produto ou nada". Devolvastd::nulloptquando não achar; teste comif (auto p = buscar(…))..value()numa caixa vazia lançastd::bad_optional_access.- Para ordenar por outro critério, copie para um
std::vectore usestd::sort(início, fim, lambda); a lambda responde "avem antes deb?". - CSV:
std::ofstream+<<para gravar;std::ifstream+getline(campos, x, ',')+stoi/stodpara ler. Capture a exceção de conversão por linha, senão uma linha ruim derruba o programa.