Sistema de Gerenciamento de Imóveis em C++

Uma explicação detalhada e interativa sobre as funcionalidades e o código-fonte.

Autores: Gabriel Henrique Silva Pereira e Otávio de Oliveira.

Visão Geral do Projeto

Este projeto, desenvolvido em C++, é um sistema robusto para o gerenciamento de imóveis. Ele simula as operações de um banco de dados, permitindo a inclusão, exclusão, busca e análise estatística de dados de imóveis, utilizando um arquivo de texto simples como persistência. É um excelente exemplo de como C++ pode ser usado para desenvolver aplicações de console com manipulação de dados e arquivos.

O que o Sistema Faz?

Componentes Principais Utilizados:

Estrutura `Imovel`

A base de todo o sistema é a estrutura `Imovel`. Ela define todos os atributos que um imóvel pode ter, essencialmente atuando como um "esquema" para cada registro de imóvel no nosso 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> é uma biblioteca poderosa em C++ que permite tratar strings como se fossem streams de entrada ou saída, similar ao cin e cout. Isso é extremamente útil para analisar (parsear) dados de uma string ou para construir uma string formatada.

Como Funciona o Stringstream na Função `carregarImoveis()`:

No nosso programa, o stringstream é vital para ler as linhas do arquivo BD_Imoveis2.txt. Cada linha contém múltiplos dados (tipo, finalidade, endereço, valor, etc.) separados por espaços. O stringstream nos permite extrair esses dados de forma sequencial e 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` para o Professor:

Como Funciona o Stringstream na Função `salvarImoveis()`:

Embora stringstream não seja usado diretamente na `salvarImoveis` para *montar* a string (usamos `ofstream` diretamente), o conceito de escrever dados formatados com espaços é similar. Para fins de explicação, você pode mencionar o conceito oposto de 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
}

Para o professor: "Enquanto na leitura usamos `>>` para extrair, na escrita (`salvarImoveis`), usamos o operador `<<` (operador de inserção) diretamente no objeto `ofstream` (`arquivo`). Ele funciona de forma análoga, pegando os valores de cada atributo do imóvel e os escrevendo no arquivo, intercalando com espaços `' '` para que os dados possam ser facilmente lidos de volta pelo `stringstream` depois. Para os booleanos, usamos um operador ternário `(condicao ? 'se_verdadeiro' : 'se_falso')` para converter `true` em 'sim' e `false` em 'não' antes de escrever no arquivo."

Funções Principais

O programa é modularizado em diversas funções, cada uma com uma responsabilidade específica.

carregarImoveis(const string& nomeArquivo)

Esta função é a primeira a ser chamada no `main()`. Ela é responsável por ler os dados dos imóveis do arquivo de texto (`BD_Imoveis2.txt`) e carregá-los para a memória (o array `imoveis`).

salvarImoveis(const string& nomeArquivo)

Esta função é crucial para a persistência dos dados. Ela é chamada antes do programa finalizar para escrever todas as informações dos imóveis que estão na memória de volta para o arquivo.

excluir(int indice)

Remove um imóvel do array `imoveis` em um determinado índice. Esta função demonstra uma operação comum em estruturas de dados baseadas em arrays fixos.

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

Explicação Detalhada: "A função `excluir(int indice)` é responsável por remover um imóvel do nosso array `imoveis`. É importante notar que em C++ com arrays estáticos, não há uma função 'remover' nativa que redimensione o array. Em vez disso, a exclusão é lógica: nós 'movemos' os elementos subsequentes uma posição para a esquerda para 'cobrir o buraco' deixado pelo elemento excluído. O loop `for` faz exatamente isso: ele pega o imóvel que está no índice `i + 1` e o copia para a posição `i`, efetivamente sobrescrevendo o imóvel que queremos excluir e deslocando todos os outros. Por fim, `totalImoveis--` diminui o contador para que o último elemento duplicado não seja mais considerado parte do nosso banco de dados."

Demonstração da Exclusão:

Array Original:

Após Excluir Índice 1 (Casa B):

incluirImovel()

Solicita ao usuário os dados de um novo imóvel e o adiciona ao array `imoveis`. Esta função faz uso extensivo de validação de entrada.

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

Todas as funções de busca seguem um padrão similar: elas iteram sobre o array `imoveis` e aplicam um critério de filtro específico para cada busca.

gerarEstatisticas()

Calcula e exibe diversas estatísticas sobre os imóveis cadastrados. Demonstra como aggregated data can be derived from the stored records.

Fluxo Completo do Programa

Compreender o fluxo de execução é fundamental para entender como as partes do sistema interagem. O programa segue uma sequência lógica desde o seu início até o seu término.

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

Para o Professor: "O programa inicia em `main()`, onde a primeira ação é chamar `carregarImoveis()` para carregar os dados persistidos do arquivo `BD_Imoveis2.txt` para a memória. Em seguida, entramos no coração do programa: o loop principal da função `menu()`. Este loop continua a exibir opções ao usuário (incluir, buscar, excluir, etc.) até que ele decida sair. Cada opção de menu chama uma função específica que realiza a operação desejada. É importante notar que todas as operações trabalham diretamente com o array de imóveis na memória. Somente quando o usuário escolhe a opção de sair (0), a função `salvarImoveis()` é invocada para persistir todas as alterações feitas no banco de dados, escrevendo-as de volta no arquivo. Isso garante que os dados não sejam perdidos ao fechar o programa."

Recursos Adicionais para Estudo

Para aprofundar seu conhecimento em C++ e nas bibliotecas usadas neste projeto, recomendamos os seguintes recursos:

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?