Sistema de Gerenciamento de Imóveis em C++

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)

Visão Geral do Projeto

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.

O que o meu sistema faz

Componentes que eu usei

Estrutura `Imovel`

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 
};

Detalhes de Cada Atributo:

Entendendo o Stringstream (`<sstream>`)

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.

Como eu uso o stringstream no 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++;
}

Explicação detalhada do ss

Como eu salvo os dados no 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".

Funções Principais

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).

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.

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.

Demonstração da Exclusão:

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.

Funções de Busca (`buscarPorRua()`, `buscarPorFaixaValor()`, `buscarPorCaracteristicas()`, `buscarPorQuartosSuites()`)

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.

gerarEstatisticas()

Calcula e mostra várias estatísticas dos imóveis cadastrados. É onde eu transformo os registros guardados em informação agregada.

Fluxo Completo do Programa

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.

Início do Programa (main())
Chama carregarImoveis("BD_Imoveis2.txt")
(Carrega dados do arquivo para a memória)
Loop Principal do menu()
(Exibe opções ao usuário)
Usuário Escolhe uma Opção
(Ex: Incluir, Buscar, Excluir, Estatísticas)
Chama a Função Correspondente
(Ex: incluirImovel(), buscarPorRua(), gerarEstatisticas())
Executa a Operação Solicitada
Retorna ao menu()
(Loop Continua...)
Usuário Escolhe Sair (Opção 0)
Chama salvarImoveis("BD_Imoveis2.txt")
(Salva dados da memória de volta no arquivo)
Fim do Programa

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.

Recursos Adicionais para Estudo

Os recursos que eu usei (e recomendo) pra ir mais fundo em C++ e nas bibliotecas desse projeto:

Quiz Rápido

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?