Uma explicação detalhada e interativa sobre as funcionalidades e o código-fonte.
Autores: Gabriel Henrique Silva Pereira e Otávio de Oliveira.
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.
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
};
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 nosso 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>
é 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.
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++;
}
stringstream ss(linha);
Esta é a linha chave! Ela pega toda a string linha
(que contém uma linha completa de dados do arquivo, como "casa venda rua_silva...") e a transforma em um "fluxo" de dados. Pense nisso como uma fita de áudio onde cada pedaço de informação é uma "música" separada por pausas (espaços em branco). O stringstream
nos permite "tocar" essa fita e extrair cada música.
ss >> im.tipo;
O operador >>
(operador de extração) é usado aqui. Ele lê o próximo "pedaço" de dados do stringstream
(o nosso 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;
Podemos encadear o operador >>
para extrair múltiplos valores sequencialmente. O stringstream
"lembra" onde parou na linha e continua lendo o próximo dado para cada variável, um após o outro, separados por espaços.
ss >> temp; im.cozinha = (temp == "sim");
Para os campos booleanos (`bool`), como cozinha
, não podemos ler diretamente "sim" ou "não" para um bool
. Então, primeiro lemos a palavra ("sim" ou "não") para uma variável temporária do tipo string
(temp
). Em seguida, usamos a expressão `(temp == "sim")`. Se temp
for "sim", essa expressão resulta em `true`, que é então atribuído a `im.cozinha`. Caso contrário, resulta em `false`.
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."
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`).
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)
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.
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` 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."
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.
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 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.
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 exibe diversas estatísticas sobre os imóveis cadastrados. Demonstra como aggregated data can be derived from the stored records.
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.
main()
)carregarImoveis("BD_Imoveis2.txt")
menu()
incluirImovel()
, buscarPorRua()
, gerarEstatisticas()
)menu()
salvarImoveis("BD_Imoveis2.txt")
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."
Para aprofundar seu conhecimento em C++ e nas bibliotecas usadas neste projeto, recomendamos os seguintes recursos:
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?