Uma explicação detalhada e interativa do meu sistema — como ele funciona e como eu escrevi o código.
Autores: Gabriel Henrique Silva Pereira (RA: 2025.1.08.036) e Otávio de Oliveira (RA: 2025.1.08.034)
Esse é um sistema que eu desenvolvi em C++ pra gerenciar imóveis. Eu fiz ele funcionar como um banco de dados: dá pra incluir, excluir, buscar e gerar estatísticas dos imóveis, usando um arquivo de texto simples como persistência. Foi aqui que eu botei pra rodar manipulação de dados e arquivos numa aplicação de console.
A base de tudo é a struct Imovel, que eu criei pra definir todos os atributos que um imóvel pode ter. Ela é o "esquema" de cada registro no meu banco de dados.
struct Imovel {
string tipo, finalidade, endereco, bairro, cidade; // Informações básicas do imóvel
float area, valor, iptu; // Área, valor e IPTU do imóvel
int quartos, suites, banheiros, vagas; // Quantidade de quartos, suítes, banheiros e vagas
bool cozinha, sala, varanda, areaServico; // Presença de cômodos
string piso, conservacao; // Tipo de piso e estado de conservação
bool armarios, arCondicionado, aquecedor, ventilador; // Características adicionais
};
string tipo, finalidade, endereco, bairro, cidade;
São do tipo string, ideais para armazenar informações textuais como "casa", "apartamento", "venda", "aluguel", o endereço completo, o bairro e a cidade. Não há um limite fixo de caracteres, o que é flexível.
float area, valor, iptu;
São do tipo float, permitindo armazenar números decimais. Usados para a área do imóvel em metros quadrados, o valor de venda/aluguel e o valor do IPTU. É importante usar float (ou double para maior precisão) para valores que podem ter casas decimais.
int quartos, suites, banheiros, vagas;
São do tipo int, usados para armazenar contagens inteiras. Representam a quantidade de quartos, suítes, banheiros e vagas de garagem. Esses valores geralmente não têm frações, por isso int é o tipo apropriado.
bool cozinha, sala, varanda, areaServico;
São do tipo bool (booleano), que armazena apenas dois estados: true (verdadeiro) ou false (falso). No meu arquivo de texto, esses são representados por "sim" ou "não". São usados para indicar a presença ou ausência de certos cômodos ou características no imóvel.
string piso, conservacao;
Também são do tipo string, para armazenar descrições textuais sobre o tipo de piso (ex: "cerâmica", "madeira") e o estado de conservação do imóvel (ex: "bom", "ruim", "excelente").
bool armarios, arCondicionado, aquecedor, ventilador;
Similar aos outros campos bool, representam a presença de características adicionais como armários embutidos, ar-condicionado, aquecedor e ventilador. No arquivo, são lidos como "sim" ou "não" e convertidos para true ou false.
O <sstream> foi a peça que eu escolhi pra tratar strings como se fossem streams de entrada e saída, igual ao cin e cout. Eu uso ele pra parsear os dados de cada linha do arquivo — e daria pra usar também pra montar uma string formatada.
carregarImoveis()No meu programa, o stringstream é peça-chave pra ler as linhas do BD_Imoveis2.txt. Cada linha tem vários dados (tipo, finalidade, endereço, valor, etc.) separados por espaços, e o stringstream me deixa extrair tudo em sequência, já com a conversão de tipo automática.
// Exemplo de uma linha do arquivo:
// casa venda rua_silva centro cidade_a 120.5 250000 3 1 2 2 sim sim nao sim ceramica bom sim nao sim nao
while (getline(arquivo, linha) && totalImoveis < MAX_IMOVEIS) {
stringstream ss(linha); // 1. Cria um stringstream a partir da linha
Imovel& im = imoveis[totalImoveis];
ss >> im.tipo; // 2. Extrai o primeiro dado (tipo)
if (im.tipo == "fim") break;
ss >> im.finalidade >> im.endereco >> im.bairro >> im.cidade; // 3. Extrai mais strings
ss >> im.area >> im.valor >> im.iptu; // 4. Extrai floats
ss >> im.quartos >> im.suites >> im.banheiros >> im.vagas; // 5. Extrai ints
string temp;
ss >> temp; im.cozinha = (temp == "sim"); // 6. Extrai "sim"/"não" e converte para bool
// ... e assim por diante para os outros campos booleanos e strings.
totalImoveis++;
}
ssstringstream ss(linha);
Essa é a linha-chave! Ela pega a string linha inteira (uma linha completa do arquivo, tipo "casa venda rua_silva...") e transforma num "fluxo" de dados. Eu penso nisso como uma fita onde cada informação é uma "música" separada por pausas (os espaços). O stringstream me deixa "tocar" essa fita e extrair cada pedaço.
ss >> im.tipo;
O operador >> (operador de extração) é usado aqui. Ele lê o próximo "pedaço" de dados do stringstream (o meu ss) até encontrar um espaço em branco e o armazena na variável im.tipo. O stringstream avança automaticamente para o próximo pedaço da linha. Se im.tipo for uma string, ele armazena o texto. Se for um tipo numérico (`float` ou `int`), ele tenta converter o texto para esse número.
ss >> im.finalidade >> im.endereco >> im.bairro >> im.cidade;
Dá pra encadear o operador >> e extrair vários valores de uma vez. O stringstream "lembra" onde parou na linha e segue lendo o próximo dado pra cada variável, um atrás do outro, separados por espaços.
ss >> temp; im.cozinha = (temp == "sim");
Pros campos booleanos (bool), tipo cozinha, eu não consigo ler direto "sim" ou "não" pra um bool. Então eu leio a palavra pra uma string temporária (temp) e uso (temp == "sim"): se temp for "sim" isso vira true e vai pro im.cozinha; senão, vira false.
salvarImoveis()Na hora de salvar eu não uso stringstream pra montar a string — escrevo direto no ofstream. Mas a ideia de gravar os dados formatados com espaços é a mesma, só que no sentido contrário (inserção):
// Dentro da função salvarImoveis()
for (int i = 0; i < totalImoveis; ++i) {
Imovel& im = imoveis[i];
// Operador '<<' (inserção) escreve os dados no arquivo, com espaços.
arquivo << im.tipo << ' ' << im.finalidade << ' ' << im.endereco << ' ' << im.bairro << ' ' << im.cidade << ' ';
arquivo << im.area << ' ' << im.valor << ' ' << im.iptu << ' ';
// ... e converte booleanos de volta para "sim" ou "não"
arquivo << (im.cozinha ? "sim" : "não") << ' ';
// ...
arquivo << '\n'; // Quebra de linha para o próximo imóvel
}
Resumindo o meu raciocínio: na leitura eu uso >> pra extrair; na escrita (salvarImoveis) eu uso << direto no ofstream (arquivo). Funciona de forma análoga — eu pego cada atributo do imóvel e escrevo no arquivo intercalando com espaços ' ', pra depois conseguir ler tudo de volta com o stringstream. Pros booleanos eu uso um ternário (condicao ? 'verdadeiro' : 'falso') pra transformar true em "sim" e false em "não".
Eu modularizei o programa em várias funções, cada uma com uma responsabilidade só.
carregarImoveis(const string& nomeArquivo)Essa é a primeira função que eu chamo no main(). Ela lê os dados dos imóveis do arquivo de texto (BD_Imoveis2.txt) e carrega tudo pra memória (o array imoveis).
ifstream arquivo(nomeArquivo);: Abre o arquivo especificado para leitura. `ifstream` é a classe para entrada de arquivo.if (!arquivo.is_open()): Verifica se o arquivo foi aberto com sucesso. Se não for encontrado, ele notifica o usuário e o programa continua com um banco de dados vazio.getline(arquivo, linha);: Lê uma linha completa do arquivo. A primeira linha geralmente é ignorada (cabeçalho).while (getline(arquivo, linha) && totalImoveis < MAX_IMOVEIS): Continua lendo linha por linha até o final do arquivo ou até atingir o limite `MAX_IMOVEis`.stringstream ss(linha); e ss >> ...: Como explicado na seção anterior, é aqui que cada linha é parseada e os dados são atribuídos aos membros da estrutura `Imovel`.arquivo.close();: Fecha o arquivo após a leitura. Essencial para liberar recursos.salvarImoveis(const string& nomeArquivo)Essa é a função que garante a persistência. Eu chamo ela antes do programa fechar pra escrever tudo que está na memória de volta no arquivo.
ofstream arquivo(nomeArquivo);: Abre o arquivo especificado para escrita. `ofstream` é a classe para saída de arquivo. Se o arquivo não existir, ele será criado. Se existir, seu conteúdo será sobrescrito.if (!arquivo.is_open()): Verifica se o arquivo foi aberto para escrita com sucesso.arquivo << "tipo finalidade ...\n";: Escreve uma linha de cabeçalho no início do arquivo, o que ajuda na legibilidade e na depuração.for (int i = 0; i < totalImoveis; ++i): Itera sobre todos os imóveis atualmente no array `imoveis`.arquivo << im.tipo << ' ' << im.finalidade << ' ' << ... << '\n';: Para cada imóvel, os dados de seus atributos são escritos no arquivo, separados por espaços, e uma quebra de linha `\n` é adicionada para o próximo imóvel. Os booleanos são convertidos para "sim" ou "não" (`(im.cozinha ? "sim" : "não")`).arquivo << "fim" << endl;: Adiciona um marcador "fim" no final do arquivo, que é usado por `carregarImoveis` para saber quando parar de ler.arquivo.close();: Fecha o arquivo, garantindo que todos os dados sejam gravados e liberando o recurso.excluir(int indice)Remove um imóvel do array imoveis num índice específico. Aqui eu mostro como lido com remoção num array estático.
void excluir(int indice) {
// Loop que começa do índice do imóvel a ser excluído até o penúltimo imóvel.
for (int i = indice; i < totalImoveis - 1; ++i)
imoveis[i] = imoveis[i + 1]; // Copia o imóvel da próxima posição para a posição atual, "movendo" todos os imóveis para a esquerda.
totalImoveis--; // Decrementa o contador total de imóveis.
}
Como eu fiz: a excluir(int indice) tira um imóvel do array imoveis. Em C++ com array estático não existe um "remover" nativo que redimensione, então a exclusão que eu fiz é lógica: eu "movo" os elementos seguintes uma posição pra esquerda pra cobrir o buraco que sobrou. O for faz exatamente isso — pega o imóvel do índice i + 1 e copia pra posição i, sobrescrevendo o que eu quero excluir e deslocando o resto. No fim, totalImoveis-- diminui o contador pra ignorar o último elemento duplicado.
Array Original:
Após Excluir Índice 1 (Casa B):
incluirImovel()Pede os dados de um novo imóvel pro usuário e adiciona no array imoveis. Aqui eu caprichei na validação de entrada.
getline(cin >> ws, im.endereco);: O cin >> ws é uma técnica para limpar o buffer de entrada antes de ler uma linha com espaços, evitando que um `\n` remanescente de uma leitura anterior cause problemas.totalImoveis++;: Incrementa o contador após adicionar o novo imóvel.Todas as minhas buscas seguem o mesmo padrão: eu percorro o array imoveis e aplico um filtro específico pra cada tipo de busca.
buscarPorRua(): Compara o endereço digitado pelo usuário (usando `enderecoBuscado == im.endereco`).buscarPorFaixaValor(): Verifica se o `valor` do imóvel está entre os limites mínimo e máximo fornecidos pelo usuário (`im.valor >= valorMin && im.valor <= valorMax`).buscarPorCaracteristicas(): Verifica múltiplas condições booleanas (ex: `im.armarios && im.arCondicionado`).buscarPorQuartosSuites(): Compara a quantidade de quartos e suítes (`im.quartos >= minQuartos && im.suites >= minSuites`).gerarEstatisticas()Calcula e mostra várias estatísticas dos imóveis cadastrados. É onde eu transformo os registros guardados em informação agregada.
Pra entender como as partes do sistema conversam, vale acompanhar o fluxo de execução. O programa segue uma sequência lógica do começo ao fim.
main())carregarImoveis("BD_Imoveis2.txt")menu()incluirImovel(), buscarPorRua(), gerarEstatisticas())menu()salvarImoveis("BD_Imoveis2.txt")Resumo do fluxo: o programa começa no main(), e a primeira coisa que eu faço é chamar carregarImoveis() pra trazer os dados do BD_Imoveis2.txt pra memória. Depois eu entro no coração do sistema: o loop da função menu(), que fica mostrando as opções (incluir, buscar, excluir, etc.) até o usuário decidir sair. Cada opção chama a função certa pra fazer a operação. Tudo trabalha direto no array em memória — só quando o usuário escolhe sair (0) é que eu chamo salvarImoveis() pra gravar tudo de volta no arquivo. Assim nada se perde quando o programa fecha.
Os recursos que eu usei (e recomendo) pra ir mais fundo em C++ e nas bibliotecas desse projeto:
cin, cout).ifstream, ofstream).stringstream).1. Qual biblioteca é usada para manipular strings como fluxos de entrada/saída em C++?
2. O que o operador >> faz em um stringstream?
3. Qual é a principal finalidade das funções carregarImoveis e salvarImoveis?
4. Na função excluir(), como um imóvel é removido de um array estático?