Polimorfismo

Padrão

Olá pessoal! Hoje encerramos a nossa série sobre orientação a objetos… sim, como eu disse lá no primeiro post, esta série é bem mais curta que a nossa primeira, sobre Programação para Iniciantes. Mas acredito que ao longo destes 5 posts, foi possível pelo menos dar uma noção sobre orientação a objetos. Obviamente, não tratei de conceitos complexos (afinal eu nem sou autoridade no assunto pra abordar tópicos muito avançados).

Então, sem mais delongas, vamos falar sobre o tal do Polimorfismo. Sim… um nome “feio”, mas nem por isso é complicado. Praticamente, todo o conceito que envolve o polimorfismo já foi visto no último post, sobre métodos e classes abstratas.

Mas o que vem a ser o polimorfismo (polymorphism, em inglês) é uma técnica que permite que referências de tipos de classes mais abstratas representem o comportamento das classes concretas que referenciam. Dessa forma, é possível tratar vários tipos de maneira homogênea (através da interface do tipo mais abstrato).

Continue lendo!

Datas em C++

Padrão

Olá pessoal!

Vasculhando minhas coisas, achei aqui uma classe bem simples em C++ para trabalhar com datas. Está bem “rústico”, já que quando eu fiz (uns 2 anos atrás), não tinha muita noção sobre boas práticas de programação. Mas, de qualquer forma, já serve para dar uma ajuda em projetos pequenos. Ah, os métodos são todos estáticos, de forma que você não precisa instanciar a classe para utilizá-los 😉

Continue lendo!

Classes e Métodos Abstratos

Padrão

Uma Classe Abstrata é desenvolvida para representar entidades e conceitos abstratos. A classe abstrata é sempre uma superclasse que não possui instâncias (ou melhor, não pode ser instanciada). Ela define um modelo para determinada funcionalidade e geralmente fornece uma implementação incompleta – a parte genérica – dessa funcionalidade. Caso a classe abstrata não possua nenhuma implementação (possuindo apenas as assinaturas dos métodos), ela também pode ser chamada de Interface. Cada uma das classes derivadas da classe abstrata completa a funcionalidade da classe abstrata, adicionando um comportamento específico.

Geralmente, uma classe abstrata possui métodos abstratos. Esses métodos são implementados em suas subclasses com o objetivo de definir seu comportamento específico. O método abstrato define apenas a assinatura do método e, portanto, não tem código.

No caso do Java, em específico, ele diferencia uma classe abstrata de uma interface. Vamos ver no exemplo a seguir, como ficaria a implementação de uma classe abstrata em C++…

#include <iostream>
using namespace std;

class Animal
{
public:
    virtual void comer() = 0;
    // Superclasses devem ter o destrutor virtual
    virtual ~Animal()
    {
    }
};

class Lobo: public Animal
{
public:
    void comer()
    {
        cout << "Eu me alimento como um lobo!" << endl;
    }
};

class Peixe: public Animal
{
public:
    void comer()
    {
        cout << "Eu me alimento como um peixe!" << endl;
    }
};

int main(void)
{
    Animal *a = new Lobo();
    Animal *b = new Peixe();

    a->comer();
    b->comer();

    return 0;
}

…e em Java…

package teste;

abstract class Animal {

    public abstract void comer();
}

class Lobo extends Animal {

    @Override
    public void comer() {
        System.out.println("Eu me alimento como um lobo!");
    }
}

class Peixe extends Animal {

    @Override
    public void comer() {
        System.out.println("Eu me alimento como um peixe!");
    }
}

public class Teste {

    public static void main(String[] args) {
        Animal a = new Lobo();
        Animal b = new Peixe();

        a.comer();
        b.comer();
    }
}

Percebam, primeiramente, que em C++ a declaração da classe abstrata não ocorre explicitamente. Ela se torna abstrata pelo fato de possuir métodos sem implementação. Se declararmos um método em C++ como virtual e o implementarmos, fazemos com que a implementação dele por parte da subclasse torne-se opcional (o que não ocorre neste caso).

Ao herdarmos nas classes Lobo e Peixe as características da classe Animal, estamos automaticamente obrigando-as a implementar o método abstrato comer(), específico para cada uma das subclasses. Enquanto em C++ declaramos o método como virtual e colocamos um = 0 após a sua declaração, em Java precisamos especificar explicitamente que tanto a classe quanto o método são abstratos, através da palavra-chave abstract. Na hora de implementarmos em C++, não é necessário nada adicional, enquanto que em Java, é necessário adicionar a anotação @Override.

Apesar de não podermos instanciar a classe abstrata, podemos declará-la como um tipo, que aceitará qualquer uma de suas implementações (como ocorre nos exemplos). Porém, nesse caso, se as subclasses tiverem métodos específicos que não pertencem ao escopo da superclasse, eles não serão reconhecidos.

Em Java, ainda temos uma outra característica importante nesse sentido: o conceito de Interfaces. Na verdade, uma interface nada mais é que uma classe abstrata que possui somente assinaturas de métodos. Assim, o nosso exemplo acima ficaria da seguinte forma:

package teste;

interface Animal {

    public void comer();
}

class Lobo implements Animal {

    @Override
    public void comer() {
        System.out.println("Eu me alimento como um lobo!");
    }
}

class Peixe implements Animal {

    @Override
    public void comer() {
        System.out.println("Eu me alimento como um peixe!");
    }
}

public class Teste {

    public static void main(String[] args) {
        Animal a = new Lobo();
        Animal b = new Peixe();

        a.comer();
        b.comer();
    }
}

Se analisarmos nosso exemplo, o uso de interface na definição da classe Animal seria a alternativa mais correta. Percebam a diferença: não utilizamos a palavra-chave class, e os métodos não precisam da palavra abstract, já que isso já está implícito no conceito da interface. Além disso, ao herdarmos da interface, utilizamos a palavra implements em vez de extends.

Isso me remete a uma coisa interessante: ao contrário do C++, a linguagem Java não permite o uso de herança múltipla, ou seja, uma classe só pode estender uma outra classe. Porém, ela pode implementar diversas interfaces. Assim:

class SubClasse extends ClassA implements ClasseB {
...

Bom pessoal, é isso. No próximo post desta série sobre Orientação a Objetos, veremos o conceito de polimorfismo. Estudem o assunto deste post de hoje, pois ele é a base para o entendimento do próximo post.

Documentando Código com Doxygen

Padrão

Doxygen é um sistema de documentação C++, C, Java, Python, IDL (CORBA e variações da Microsoft), Fortran, VHDL, PHP, C# e algumas extensões de D. Bastante coisa, não? Ele é uma ferramenta open-source para geração de documentação e referências de código. A documentação é escrita através de marcações dentro do próprio código-fonte.

Ele pode ajudar de 3 maneiras:

  • ele pode gerar uma documentação online para o navegador (em HTML) e/ou referência offline (em Latex) a partir de um conjunto de códigos documentados. Também há suporte para gerar saída em RTF (Word), PostScript, PDF com links, HTML compactado e man pages do Linux. A documentação é extraída diretamente dos códigos;
  • Você pode também configurar o Doxygen para extrair a estrutura do código com base em arquivos não documentados, de forma a poder visualizar relações entre os vários elementos, o que inclui gráficos de dependência, diagramas de herança e diagramas colaborativos, todos gerados automaticamente;
  • E pode, ainda, gerar documentação padrão.

Altamente portável, roda em Linux, Windows e Mac OS X. Bastante interessante, não?

Mas como faço pra documentar o código?

A sintaxe do Doxygen segue basicamente dois estilos: baseado em Javadoc e baseado em Qt. O estilo Javadoc consiste em um bloco de comentário estilo C começando com dois *, dessa forma:

/**
 * ...texto...
 */

Já a forma em estilo Qt consiste em adicionar uma ! logo após a abertura do comentário:

/*!
 * ...texto...
 */

Existem também outras formas de sintaxe, porém menos utilizadas que estas. Todas elas podem ser encontradas no manual do Doxygen.

Estes blocos de comentários são complementados com o uso de parâmetros que fornecerão os dados necessários para a ferramenta montar a documentação. Vejamos o exemplo:

Usando o estilo Javadoc
/** @brief Descrição breve.
 *         Continuando a descrição breve.
 *
 *  Descrição detalhada começa aqui.
 */

Usando o estilo Qt
/*! \brief Descrição breve.
 *         Continuando a descrição breve.
 *
 *  Descrição detalhada começa aqui.
 */

O Doxygen possui várias tags para formatação da documentação, de forma a possibilitar, de fato, a construção de uma documentação bastante robusta. No exemplo, temos a tag brief que fornece uma breve descrição do elemento a ser documentado.

A seguir, vamos ver uma classe documentada utilizando o Doxygen tanto no estilo Javadoc quanto no estilo Qt.

/**
 *  Uma classe de teste. Uma descrição mais elaborada da classe.
 */

class Teste
{
  public:

    /**
     * Uma enumeração.
     * Descrição mais detalhada da enumeração.
     */

    enum TEnum {
          TVal1, /**< Valor enumerável TVal1. */
          TVal2, /**< Valor enumerável TVal2. */
          TVal3  /**< Valor enumerável TVal3. */
         }
       *ptrEnum, /**< Ponteiro da enumeração. Detalhes. */
       varEnum;  /**< Variável da enumeração. Detalhes. */

      /**
       * Um construtor.
       * Descrição mais elaborada do construtor.
       */
      Teste();

      /**
       * Um destrutor.
       * Descrição mais elaborada do destrutor.
       */
     ~Teste();

      /**
       * Um membro normal com dois argumentos e retornando um valor inteiro.
       * @param a um argumento inteiro.
       * @param s um ponteiro para caractere constante.
       * @see Teste()
       * @see ~Teste()
       * @see meTesteTambem()
       * @see varPublica()
       * @return Os resultados do teste
       */
       int meTeste(int a,const char *s);

      /**
       * Um membro puramente virtual.
       * @see testMe()
       * @param c1 o primeiro argumento.
       * @param c2 o segundo argumento.
       */
       virtual void meTesteTambem(char c1,char c2) = 0;

      /**
       * Uma variável pública.
       * Detalhes.
       */
       int varPublica;

      /**
       * Uma variável função.
       * Detalhes.
       */
       int (*handler)(int a,int b);
};

 

//!  Uma classe de teste.
/*!
  Uma descrição mais elaborada da classe.
*/

class Teste
{
  public:

    //! Uma enumeração.
    /*! Descrição mais detalhada da enumeração. */
    enum TEnum {
                 TVal1, /*!< Valor enumerável TVal1. */
                 TVal2, /*!< Valor enumerável TVal2. */
                 TVal3  /*!< Valor enumerável TVal3. */
               }
         //! Ponteiro da enumeração.
         /*! Detalhes. */
         *ptrEnum,
         //! Variável da enumeração.
         /*! Detalhes. */
         varEnum;

    //! Um construtor.
    /*!
      Descrição mais elaborada do construtor.
    */
    Teste();

    //! Um destrutor.
    /*!
      Descrição mais elaborada do destrutor.
    */
   ~Teste();

    //! Um membro normal com dois argumentos e retornando um valor inteiro.
    /*!
      \param a um argumento inteiro.
      \param s um ponteiro para caractere constante.
      \return Os resultados do teste
      \sa Teste(), ~Teste(), meTesteTambem() and varPublica()
    */
    int meTeste(int a,const char *s);

    //! Um membro puramente virtual.
    /*!
      \sa testMe()
      \param c1 o primeiro argumento.
      \param c2 o segundo argumento.
    */
    virtual void meTesteTambem(char c1,char c2) = 0;

    //! Uma variável pública.
    /*!
      Detalhes.
    */
    int varPublica;

    //! Uma variável função.
    /*!
      Detalhes.
    */
    int (*handler)(int a,int b);
};

Gerando a Documentação

Bom, o primeiro passo é baixar o Doxygen. No Windows, você pode baixar do site oficial mesmo, aqui. No Linux você pode baixar do site oficial, ou dos repositórios da sua distribuição (a maioria tem).

No caso especial do Windows, ele vem com o Doxywizard. Com ele, é bem fácil configurar o projeto.

No caso do Linux, o processo é um pouco mais trabalhoso, já que temos que fazer o processo de configuração manualmente. Primeiramente, navegue até a pasta onde os códigos ou o projeto está. Chegando lá, digite:

doxygen -g

Com isso, um arquivo chamado Doxygen será gerado na pasta atual. A sintaxe deste arquivo é bastante similar à sintaxe de um Makefile comum. Após a configuração dos parâmetros, basta salvar e executar:

doxygen Doxygen

Neste link você obtém mais informações sobre as configurações do arquivo.

Assim, teremos algo parecido com isso ao gerarmos a documentação do código-fonte de exemplo:

Bacana, não? Aqui você encontra um exemplo de documentação de um projeto real gerada com o Doxygen.

Espero que seja útil a vocês, assim como foi pra mim! 😀

Até a próxima!

Herança

Padrão

Olá leitores! Apesar de meio parada, a nossa série de posts sobre Orientação a Objetos continua! Hoje iremos tratar de um assunto de extrema importância dentro das técnicas de modelagem de sistemas orientados a objetos: os conceitos de Herança (ou Inheritance). Mas o que vem a ser herança?

A grosso modo, podemos dizer que programar utilizando herança é reaproveitar código que será comum a outras classes. Utilizar herança é descobrir semelhanças entre classes.

Herança lembra família… é o que é transmitido de uma geração para outra…

Projetar utilizando herança significa colocar código comum em uma classe e dizer às classes mais específicas que a classe comum (mais abstrata) é sua superclasse. Quando uma classe herda de outra, a subclasse herda da superclasse. A notação de herança em C++ é…

class SubClasse: public SuperClasse {
...
};

… e em Java…

class SubClasse extends SuperClasse {
...
}

Voltando aos conceitos, dizemos que um relacionamento de herança significa que a subclasse herdará os membros da superclasse. Os “membros de uma classe” são os seus métodos e suas variáveis de instância (ou atributos). Vamos a um exemplo prático. Temos uma classe chamada Médico que iremos especializar em Clínico Geral e Cirurgião. Na notação UML (Unified Modeling Language), teríamos algo assim:

Esta é a notação de herança na UML. A classe mãe (ou superclasse) ligada a suas filhas (subclasses) por uma linha, com um triângulo indicando a presença de herança. Bom, neste caso, temos a classe mais abstrata, chamada Médico que possui uma variável de instância chamada trabalhaNoHospital e um método chamado tratarPaciente(). As duas subclasses, Clínico Geral e Cirurgião, automaticamente possuem estes atributos em seu escopo. No caso do Clínico Geral, ele possui, adicionalmente, uma variável de instância chamada atendeEmCasa e um método receitar(), que são específicos dele. Já a classe Cirurgião sobrescreve o método tratarPaciente() e adiciona o método fazerIncisão().

Mas espere aí? O que significa sobrescrever um método?

No nosso exemplo, a superclasse Médico tem o seu método tratarPaciente(), pois é uma ação comum a todo médico. No caso da superclasse, todo médico faz um check-up em seu paciente. Esse é o comportamento comum. A classe Clínico Geral possui esse comportamento, ou seja, um médico clínico geral, ao tratar o paciente, realiza um check-up nele. Já no caso da classe Cirurgião, ele também trata o paciente. Porém, o seu tratamento consiste em realizar a cirurgia no tratamento. Apesar de ter o mesmo método, o seu comportamento é diferente da classe mãe. Assim, ela sobrescreve o método da superclasse, adicionando seu comportamento próprio.

Vamos ver, então, como ficaria isso nas linguagens C++…

class Medico
{
    bool trabalhaNoHospital;

    void tratarPaciente()
    {
        // faz um check-up
    }
};
class ClinicoGeral: public Medico
{
    bool atendeEmCasa;

    void receitar()
    {
        // para tratar problemas simples
    }
};
class Cirurgiao: public Medico
{
    void tratarPaciente()
    {
        // realizar cirurgia
    }

    void fazerIncisao()
    {
        // faz a incisão (corta o paciente!)
    }
};

… e Java…

public class Medico {

    boolean trabalhaNoHospital;

    void tratarPaciente() {
        // faz um check-up
    }
}
public class ClinicoGeral extends Medico {

    boolean atendeEmCasa;

    void receitar() {
        // para tratar problemas simples
    }
}
public class Cirurgiao extends Medico {

    void tratarPaciente() {
        // realizar cirurgia
    }

    void fazerIncisao() {
        // faz a incisão (corta o paciente!)
    }
}

Como podem perceber, a sintaxe para a realização de herança nas linguagens é bastante simples, tanto em C++, quanto em Java.

Para não estender muito o post e evitar deixá-lo demasiadamente complexo, vamos encerrando por aqui. Recomendo que, para um melhor aprendizado sobre o assunto, vocês devam procurar material extra, tentar implementar pequenas estruturas de herança, enfim, realmente praticarem. Só através da prática é que conseguirão amadurecer os conceitos e conseguirão englobá-los no desenvolvimento de seus aplicativos.

No próximo post desta série, falaremos sobre o conceito de classes e métodos abstratos, assunto bastante interessante na Orientação a Objetos.

Até lá!

Encapsulamento de Dados

Padrão

Olá! Continuando a nossa série sobre orientação a objetos, vamos falar hoje sobre Encapsulamento de Dados. Na Orientação a objetos, o encapsulamento significa separar o programa em partes, o mais isoladas possível. Dessa forma, tem-se por objetivo tornar o software mais flexível, fácil de modificar e de criar novas implementações.

No encapsulamento de dados, o que fazemos é impedir o acesso direto aos atributos da classe, criando métodos tanto para obter os valores dos atributos, quanto para atribuir valores a eles. Os métodos que realizam tais tarefas são comumente chamaos de setters e getters (dos verbos set e get, em inglês).

Dessa forma, a classe Cachorro que tínhamos em nosso primeiro post sobre OO, utilizando o encapsulamento de dados, ficaria assim:

Primeiro em C++…

// Arquivo cachorro.h
#include <iostream>
#include <string>
using namespace std;

class Cachorro {
private:
    int tamanho;
    string raca;
    string nome;
public:
    Cachorro();
    ~Cachorro();
    void setTamanho(int tamanho);
    int getTamanho();
    void setRaca(string raca);
    string getRaca();
    void setNome(string nome);
    string getNome();
    void latir();
};
// Arquivo cachorro.cpp
#include "cachorro.h"

Cachorro::Cachorro() {
}

Cachorro::~Cachorro() {
}

void Cachorro::setTamanho(int tamanho) {
    this->tamanho = tamanho;
}

int Cachorro::getTamanho() {
    return tamanho;
}

void Cachorro::setRaca(string raca) {
    this->raca = raca;
}

string Cachorro::getRaca() {
    return raca;
}

void Cachorro::setNome(string nome) {
    this->nome = nome;
}

string Cachorro::getNome() {
    return nome;
}

void Cachorro::latir() {
    cout << "Ruff! Ruff!" << endl;
}

… e depois em Java…

// Arquivo Cachorro.java
package canil.exemplo;

public class Cachorro {
    private int tamanho;
    private String raca;
    private String nome;

    public void setTamanho(int tamanho) {
        this.tamanho = tamanho;
    }

    public int getTamanho() {
        return tamanho;
    }

    public void setRaca(String raca) {
        this.raca = raca;
    }

    public String getRaca() {
        return raca;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getNome() {
        return nome;
    }

    public void latir() {
        System.out.println("Ruff! Ruff!");
    }
}

Uma das grandes vantagens do uso do encapsulamento de dados é controlar o acesso aos atributos da classe, evitando comportamentos inesperados. Por exemplo, se determinado atributo não pode ser negativo, você pode condicionar o seu método de atribuição (set) a não deixar valores negativos (não atribuindo nada ou atribuindo alguma valor válido padrão). Além disso, caso determinado valor deva ser privado e não deve possuir acesso externo à classe, basta não definir seus métodos de acesso.

Bom pessoal, é isso. Aparentemente um assunto simples, mas onipresente no projeto de sistemas utilizando a orientação a objetos. No próximo post da série, falaremos sobre outro conceito muito importante na OO: a Herança!

Até lá!

Esteganografia com Arquivos Bitmap em C++

Padrão


Olá, pessoal! Este ano na faculdade tivemos um trabalho sobre esteganografia. Pra quem não conhece, a esteganografia é uma técnica no qual você esconde uma mensagem, de forma que, se ela for interceptada, o intruso nem saberá que ela existe. Combinada com técnicas de encriptação, ela se torna uma ótima forma de se transmitir conteúdo sigiloso.

Este trabalho consistia em ler um arquivo texto, escondê-lo em um arquivo Bitmap (o arquivo Bitmap é um dos mais utilizados na esteganografia pelo fato de não possuir compactação) e depois possibilitar-se extrair a mensagem do arquivo novamente.

Bom, como podem ver, o código está meio “poluído” (confesso que no começo tentei fazer uma coisa bem organizada, mas de acordo que o código foi dando muito problema, acabou “desandando” :D). Então, disponibilizo aqui a classe em C++ que implementa esteganografia em arquivos Bitmap.

// Arquivo esteganografia.h

#ifndef ESTEGANOGRAFIA_H
#define ESTEGANOGRAFIA_H

#include <string>
#include <iostream>
#include <cstdlib>
#include <cstdio>
using namespace std;

struct RGB
{
    char verde;
    char vermelho;
    char azul;
};

struct CabecalhoBitmap
{
    char identificador[2]; // Os símbolos que definem o arquivo como bitmap (0x42 e 0x4D)
    int tamanho; // O tamanho do bitmap em bytes
    short int areaReservada1; // Reservado
    short int areaReservada2; // Reservado
    int enderecoInicial; // Offset
};

struct CabecalhoMapaBits
{
    int tamanho; // O tamanho deste cabeçalho - 40 bytes
    int largura; // Largura do bitmap em pixels
    int altura; // Altura do bitmap em pixels
    short int planosCor; // A quantidade de planos de cor utilizados. Deve ser setado para 1
    short int bpp; // Bits por pixel
    int metodoCompressao; // Método de compressão
    int tamanhoImagem; // O tamanho da imagem. Este é o tamanho bruto dos dados do bitmap. Não deve ser confundido com o tamanho da imagem
    int resolucaoHorizontal; // Resolução horizontal da imagem
    int resolucaoVertical; // Resolução vertical da imagem
    int coresNaPaleta; // O número de cores na paleta de cores
    int coresImportantes; // O número de cores importantes usadas, ou 0 se todas são importantes. Geralmente ignorado.
};

class Esteganografia
{
private:
    FILE *arquivo;
    FILE *mensagem;
    CabecalhoBitmap cabecalho;
    CabecalhoMapaBits cabDados;
    string nomeArquivo;
    char bytesDeAjuste; // 0 a 3 - o número de bytes a serem adicionados para criar uma "linha divisível por 4"

    void esconderMensagem(string mensagem);
    string extrairMensagem(void);
    void salvarBitmap(void);
public:
    RGB *pixels;
    int qtdePixels;
    Esteganografia();
    ~Esteganografia();
    void inserirMensagem(string mensagem, string arquivo);
    string extrairMensagem(string arquivo);
    void imprimirInfoArquivo(void);
};

#endif // ESTEGANOGRAFIA_H
// Arquivo esteganografia.cpp

#include "esteganografia.h"

Esteganografia::Esteganografia()
{

}

Esteganografia::~Esteganografia()
{

}

void Esteganografia::inserirMensagem(string arqmensagem, string arquivo)
{
    this->arquivo = fopen(arquivo.c_str(), "rb");
    if(!this->arquivo)
    {
        cout << endl << " Arquivo nao encontrado...";
        system("pause");
        return;
    }

    string mensagemArquivo;
    char caract;
    this->mensagem = fopen(arqmensagem.c_str(), "r");
    if(!this->mensagem)
    {
        cout << endl << " Arquivo nao encontrado...";
        system("pause");
        return;
    }
    caract = fgetc(this->mensagem);
    while(caract != EOF)
    {
        mensagemArquivo += caract;
        caract = fgetc(this->mensagem);
    }
    fclose(this->mensagem);

    nomeArquivo = arquivo;

    // Preenchimento do cabeçalho do Bitmap
    fread(&cabecalho.identificador, sizeof(cabecalho.identificador), 1, this->arquivo);
    fread(&cabecalho.tamanho, sizeof(cabecalho.tamanho), 1, this->arquivo);
    fread(&cabecalho.areaReservada1, sizeof(cabecalho.areaReservada1), 1, this->arquivo);
    fread(&cabecalho.areaReservada2, sizeof(cabecalho.areaReservada2), 1, this->arquivo);
    fread(&cabecalho.enderecoInicial, sizeof(cabecalho.enderecoInicial), 1, this->arquivo);

    // Preenchimento do Mapa de Bits
    fread(&cabDados.tamanho, sizeof(cabDados.tamanho), 1, this->arquivo);
    fread(&cabDados.largura, sizeof(cabDados.largura), 1, this->arquivo);
    fread(&cabDados.altura, sizeof(cabDados.altura), 1, this->arquivo);
    fread(&cabDados.planosCor, sizeof(cabDados.planosCor), 1, this->arquivo);
    fread(&cabDados.bpp, sizeof(cabDados.bpp), 1, this->arquivo);
    fread(&cabDados.metodoCompressao, sizeof(cabDados.metodoCompressao), 1, this->arquivo);
    fread(&cabDados.tamanhoImagem, sizeof(cabDados.tamanhoImagem), 1, this->arquivo);
    fread(&cabDados.resolucaoHorizontal, sizeof(cabDados.resolucaoHorizontal), 1, this->arquivo);
    fread(&cabDados.resolucaoVertical, sizeof(cabDados.resolucaoVertical), 1, this->arquivo);
    fread(&cabDados.coresNaPaleta, sizeof(cabDados.coresNaPaleta), 1, this->arquivo);
    fread(&cabDados.coresImportantes, sizeof(cabDados.coresImportantes), 1, this->arquivo);

    this->bytesDeAjuste = cabDados.largura % 4;

    // Posiciona no fim do arquivo
    fseek(this->arquivo, 0, SEEK_END);

    // Calcula a quantidade de pixels da imagem
    qtdePixels = cabDados.largura * cabDados.altura;
    pixels = new RGB[qtdePixels];

    // Posiciona no início da área de dados da imagem
    fseek(this->arquivo, cabecalho.enderecoInicial, SEEK_SET);

    // Jogando os pixels da imagem no vetor de RGB
    for (int larg = 0, pix = 0; pix < qtdePixels;)
    {
        // Se larg chegou ao tamanho da largura da imagem
        if (larg == cabDados.largura)
        {
            fseek(this->arquivo, bytesDeAjuste, SEEK_CUR); // "Pula" os bytes faltantes na linha
            larg = 0;
            continue; // Volta no início do for
        }

        fread(&pixels[pix++], sizeof(RGB), 1, this->arquivo); // Lê o pixel e atribui os valores à estrutura RGB
        larg++;
    }

    fclose(this->arquivo);

    unsigned long capacidade = (cabecalho.tamanho - cabecalho.enderecoInicial)/3;
    if(mensagemArquivo.size() > capacidade)
    {
        cout << "Arquivo de texto muito grande!" << endl;
        system("pause");
        return;
    }
    // Agora temos o bitmap estruturado dentro da nossa classe

    if(cabecalho.enderecoInicial != 0x36)
    {
        cout << "Tipo de arquivo invalido!" << endl;
        system("pause");
        return;
    }
    this->esconderMensagem(mensagemArquivo);
    this->salvarBitmap();

    imprimirInfoArquivo();

    delete pixels;
}

string Esteganografia::extrairMensagem(string arquivo)
{
    this->arquivo = fopen(arquivo.c_str(), "rb");
    if(!this->arquivo)
    {
        cout << endl << " Arquivo nao encontrado...";
        system("pause");
        return string("");
    }

    nomeArquivo = arquivo;

    // Preenchimento do cabeçalho do Bitmap
    fread(&cabecalho.identificador, sizeof(cabecalho.identificador), 1, this->arquivo);
    fread(&cabecalho.tamanho, sizeof(cabecalho.tamanho), 1, this->arquivo);
    fread(&cabecalho.areaReservada1, sizeof(cabecalho.areaReservada1), 1, this->arquivo);
    fread(&cabecalho.areaReservada2, sizeof(cabecalho.areaReservada2), 1, this->arquivo);
    fread(&cabecalho.enderecoInicial, sizeof(cabecalho.enderecoInicial), 1, this->arquivo);

    // Preenchimento do Mapa de Bits
    fread(&cabDados.tamanho, sizeof(cabDados.tamanho), 1, this->arquivo);
    fread(&cabDados.largura, sizeof(cabDados.largura), 1, this->arquivo);
    fread(&cabDados.altura, sizeof(cabDados.altura), 1, this->arquivo);
    fread(&cabDados.planosCor, sizeof(cabDados.planosCor), 1, this->arquivo);
    fread(&cabDados.bpp, sizeof(cabDados.bpp), 1, this->arquivo);
    fread(&cabDados.metodoCompressao, sizeof(cabDados.metodoCompressao), 1, this->arquivo);
    fread(&cabDados.tamanhoImagem, sizeof(cabDados.tamanhoImagem), 1, this->arquivo);
    fread(&cabDados.resolucaoHorizontal, sizeof(cabDados.resolucaoHorizontal), 1, this->arquivo);
    fread(&cabDados.resolucaoVertical, sizeof(cabDados.resolucaoVertical), 1, this->arquivo);
    fread(&cabDados.coresNaPaleta, sizeof(cabDados.coresNaPaleta), 1, this->arquivo);
    fread(&cabDados.coresImportantes, sizeof(cabDados.coresImportantes), 1, this->arquivo);

    this->bytesDeAjuste = cabDados.largura % 4;

    // Posiciona no fim do arquivo
    fseek(this->arquivo, 0, SEEK_END);

    // Calcula a quantidade de pixels da imagem
    qtdePixels = cabDados.largura * cabDados.altura;
    pixels = new RGB[qtdePixels];

    // Posiciona no início da área de dados da imagem
    fseek(this->arquivo, cabecalho.enderecoInicial, SEEK_SET);

    // Jogando os pixels da imagem no vetor de RGB
    for (int larg = 0, pix = 0; pix < qtdePixels;)
    {
        // Se larg chegou ao tamanho da largura da imagem
        if (larg == cabDados.largura)
        {
            fseek(this->arquivo, bytesDeAjuste, SEEK_CUR); // "Pula" os bytes faltantes na linha
            larg = 0;
            continue; // Volta no início do for
        }

        fread(&pixels[pix++], sizeof(RGB), 1, this->arquivo); // Lê o pixel e atribui os valores à estrutura RGB
        larg++;
    }

    fclose(this->arquivo);

    // Agora temos o bitmap estruturado dentro da nossa classe
    string mensagem;

    mensagem = this->extrairMensagem();

    return mensagem;
}

void Esteganografia::esconderMensagem(string mensagem)
{
    /*
     * Este método pega 3 pixels da imagem por vez. Dessa forma, teremos 9 bytes.
     * Desses 9 bytes, pegamos o bit menos significativo de 8 deles e repartimos
     * os caracteres da mensagem, letra a letra.
     */

    for(unsigned int numCaracter = 0, indice = 0; numCaracter <= mensagem.size(); numCaracter++)
    {
        this->pixels[indice].vermelho &= 254;
        this->pixels[indice].verde &= 254;
        this->pixels[indice].azul &= 254;

        this->pixels[indice].vermelho |= (mensagem[numCaracter] & 1 ? 1 : 0);
        this->pixels[indice].verde |= (mensagem[numCaracter] & 2 ? 1 : 0);
        this->pixels[indice].azul |= (mensagem[numCaracter] & 4 ? 1 : 0);
        indice++;

        this->pixels[indice].vermelho &= 254;
        this->pixels[indice].verde &= 254;
        this->pixels[indice].azul &= 254;

        this->pixels[indice].vermelho |= (mensagem[numCaracter] & 8 ? 1 : 0);
        this->pixels[indice].verde |= (mensagem[numCaracter] & 16 ? 1 : 0);
        this->pixels[indice].azul |= (mensagem[numCaracter] & 32 ? 1 : 0);
        indice++;

        this->pixels[indice].vermelho &= 254;
        this->pixels[indice].verde &= 254;

        this->pixels[indice].vermelho |= (mensagem[numCaracter] & 64 ? 1 : 0);
        this->pixels[indice].verde |= (mensagem[numCaracter] & 128 ? 1 : 0);
        indice++;
    }
}

void Esteganografia::salvarBitmap(void)
{
    arquivo = fopen(nomeArquivo.c_str(), "wb");
    if (!arquivo)
    {
        cout << " Impossivel criar/sobrescrever o arquivo." << endl;
        system("pause");
        exit(1);
    }

    fwrite(&cabecalho.identificador, sizeof(cabecalho.identificador), 1, arquivo);
    fwrite(&cabecalho.tamanho, sizeof(cabecalho.tamanho), 1, arquivo);
    fwrite(&cabecalho.areaReservada1, sizeof(cabecalho.areaReservada1), 1, arquivo);
    fwrite(&cabecalho.areaReservada2, sizeof(cabecalho.areaReservada2), 1, arquivo);
    fwrite(&cabecalho.enderecoInicial, sizeof(cabecalho.enderecoInicial), 1, arquivo);

    fwrite(&cabDados.tamanho, sizeof(cabDados.tamanho), 1, arquivo);
    fwrite(&cabDados.largura, sizeof(cabDados.largura), 1, arquivo);
    fwrite(&cabDados.altura, sizeof(cabDados.altura), 1, arquivo);
    fwrite(&cabDados.planosCor, sizeof(cabDados.planosCor), 1, arquivo);
    fwrite(&cabDados.bpp, sizeof(cabDados.bpp), 1, arquivo);
    fwrite(&cabDados.metodoCompressao, sizeof(cabDados.metodoCompressao), 1, arquivo);
    fwrite(&cabDados.tamanhoImagem, sizeof(cabDados.tamanhoImagem), 1, arquivo);
    fwrite(&cabDados.resolucaoHorizontal, sizeof(cabDados.resolucaoHorizontal), 1, arquivo);
    fwrite(&cabDados.resolucaoVertical, sizeof(cabDados.resolucaoVertical), 1, arquivo);
    fwrite(&cabDados.coresNaPaleta, sizeof(cabDados.coresNaPaleta), 1, arquivo);
    fwrite(&cabDados.coresImportantes, sizeof(cabDados.coresImportantes), 1, arquivo);

    char fimDeLinha[bytesDeAjuste];

    for (int i = 0; i < bytesDeAjuste; i++)
        fimDeLinha[i] = 0;

    for (int pix = 0, larg = 0; pix < qtdePixels;)
    {
        if (larg == cabDados.largura)
        {
            fwrite(&fimDeLinha, bytesDeAjuste, 1, arquivo);
            larg = 0;
            continue;
        }
        fwrite(&pixels[pix++], sizeof(RGB), 1, arquivo);
        larg++;
    }

    fclose(arquivo);
}

string Esteganografia::extrairMensagem(void)
{
    string mensagem;
    char caracter;
    int indice = 0;

    /*
     * A cada 3 pixels (9 bytes), pegamos os bits menos significativos
     * dos 8 primeiros bytes - da mesma forma como foi gravado
     */

    do
    {
        caracter = (pixels[indice].vermelho & 1) +
                   ((pixels[indice].verde & 1) * 2) +
                   ((pixels[indice].azul & 1) * 4) +
                   ((pixels[indice+1].vermelho  & 1) * 8 ) +
                   ((pixels[indice+1].verde & 1) * 16) +
                   ((pixels[indice+1].azul & 1) * 32) +
                   ((pixels[indice+2].vermelho & 1) * 64) +
                   ((pixels[indice+2].verde & 1) * 128);

        mensagem += caracter;
        indice += 3;
    }
    while (caracter != '\0');

    mensagem = mensagem.substr(0, mensagem.length() - 1);
    return mensagem;
}

void Esteganografia::imprimirInfoArquivo(void)
{
    printf("\n INFORMACOES DO BITMAP\n\n");
    printf(" Identificador: %c%c\n", cabecalho.identificador[0], cabecalho.identificador[1]);
    printf(" Tamanho: %i\n", cabecalho.tamanho);
    printf(" Endereco Inicial: 0x%X\n", cabecalho.enderecoInicial);
    printf(" Area Reservada 1: %hi\n", cabecalho.areaReservada1);
    printf(" Area Reservada 2: %hi\n", cabecalho.areaReservada2);
    printf(" Altura: %i\n", cabDados.altura);
    printf(" Largura: %i\n", cabDados.largura);
    printf(" Pixels: %i\n", qtdePixels);
    printf(" Bytes de ajuste: %i\n ", bytesDeAjuste);
    system("pause");
}