Ponto V!

Home C/C++ Conceitos Básicos C++ Type Casting - 1ª Parte
Bruno Crivelari Sanches
C++ Type Casting - 1ª ParteImprimir
Escrito por Bruno Crivelari Sanches

A linguagem C++ (assim como a linguagem C) possui operações de casting de tipos (Type Casting) que é uma operação em que se converte uma expressão de um determinado tipo para outro tipo, essas conversões podem ser feitas de duas maneiras: implícita e explicita.

Conversão Implícita

Nesse caso, o programador não precisa fazer nada, pois a conversão de dados é feita automaticamente sem que ele precise usar qualquer instrução extra:

int main(int, char **) 
{ 
    char a = 5; 
    int i = a; 
}

No código acima não existe nada demais, certo? O único detalhe é que quando fazemos i = a, essa não é uma atribuição comum, o compilador precisa gerar código extra para converter o char para int. Esta conversão é feita de maneira implícita e silenciosa pelo compilador porque qualquer valor de char pode ser armazena em um int. Já um int, não pode ter todos os seus valores armazenados em um char. Se pegarmos como exemplo um compilador 32 bits, onde o char geralmente tem 8 bits, os seus valores vão de -128 a 127, já um int (32 bits) pode possuir valores de -2147483648 a 2147483647.

Se fizermos ao contrário:

int main(int, char **) 
{ 
    int i = 2147483647; 
    char a = i;
}

Alguns compiladores vão gerar avisos com o código acima (no caso do Visual, tem que mudar o Warning level de 3 para 4). O problema do código acima é que quando atribuímos um valor de i para a, a variável char não tem como armazenar todos os valores de um inteiro, nesse caso, pode haver uma perda de dados.

Conversão Explicita

Se você estiver usando o compilador com warnings no máximo, é possível evitar o aviso usando o código abaixo:

int main(int, char **) 
{ 
    int i = 2147483647; 
    char a = (char)i;
}

O código gerado acima vai ser idêntico ao anterior, o cast nesse caso não faz nada em relação ao código gerado, você apenas esta sinalizando: "Cale a boca compilador que eu sei o que estou fazendo". O compilador entende o seu recado e não lhe da mais aviso nenhum.

Agora vamos provocar a ira do compilador:

#include <iostream>
#include <string>

int main(int, char **) 
{ 
    using namespace std;

    vector v; 
    string *str = &v;

    *str = "abc";

    return 0; 
}

O código acima deu um belo de um erro..., isso ocorre porque o C++ tenta evitar que o programador faça coisas erradas, vamos tentar consertar a situação:

#include <iostream>
#include <string>

int main(int, char **) 
{ 
    using namespace std;

    vector v; 
    string *str = (string*)&v;

    *str = "abc";

    return 0; 
}

Agora novamente o compilador ficou quieto, mas o que aconteceu? Quase nada, quando colocamos o (string*) estamos dizendo ao compilador: "Ei, o valor da expressão &v é o endereço de uma string". O compilador simplesmente aceita (afinal nós sabemos o que estamos fazendo) e copia o valor para o ponteiro str. Atenção ao detalhe "copia o endereço", pois lembre-se que ponteiros no fundo são apenas um unsigned int com propriedades quase magicas. Note também que não foi feito nada quanto a conversão de tipo.

O que vai acontecer quando esse código for executado? Pode acontecer qualquer coisa:

  • O código pode simplesmente funcionar por alguma obra do acaso;
  • Dar uma falha de segmentação;
  • Seu computador explodir;
  • O cachorro do vizinho passar mal

No meu caso, deu um baita erro de assertion failed. Segunda a especificação, esse tipo de operação tem como resultado "undefined" ou seja, indefinido. Mas note que o problema não esta no casting, ele é totalmente valido, o problema ocorre quando tentamos jogar "abc" dentro de str. Nesse caso o operador de atribuição da string vai ser chamado, mas o ponteiro this dele ao invés de apontar para um objeto string, vai estar apontando para o vector!

Exemplo: Sistema de Eventos

Se isso causa apenas problemas, porque existe então? Bom, isso permite fazer algumas coisas interessantes... vamos imaginar que você esta criando um jogo, e nesse jogo você possui uma lista de objetos, que podem ser qualquer coisa: um monstro, um tiro, uma porta, chave, etc.

Todos esses objetos possuem uma classe base em comum, chamada Objeto:

enum Eventos_e 
{ 
    TIRO, 
    ABRIR_PORTA 
};

class Objeto 
{ 
    public: 
        virtual void Evento(int tipo, void *param) = 0; 
        virtual void Atualizar()=0; 
};

class Monstro: public Objeto 
{ 
    public: 
        virtual void Evento(int tipo, void *param); 
        virtual void Atualizar();

    private: 
        int m_energia; 
};

class Tiro: public Objeto 
{ 
    public: 
        virutal void Evento(int tipo, void *param); 
        virtual void Atualizar();

        int Potencia();

    private: 
        Objeto *MovimentarEColidir(); 
};

Agora, vamos imaginar que o monstro estava andando e levou um tiro de laser. Quando qualquer coisa acontece no nosso jogo, chamamos o método Evento e passamos as informações necessárias. O método atualizar é chamado o tempo todo para que os objetos se atualizem (como por exemplo movimentar o tiro, andar com o monstro, etc).

Vamos agora implementar o método Atualizar do tiro:

void Tiro::Atualizar() 
{ 
    Objeto *obj = MovimentarEColidir(); 
    if(obj) 
    { 
        obj->Evento(TIRO, this); 
    } 
}

O método é bem simples, vamos assumir que o método MovimentarEColidir atualiza a posição do tiro e verifica se ele bateu com algo, caso isso ocorra, o objeto com o qual ele colidiu é retornado. Quando isso ocorre, o tiro envia um evento ao objeto avisando-o do ocorrido, dessa forma não precisamos poluir a interface da classe Objeto com todo o tipo de evento que pode acontecer (tiro, abrir, usar item, etc). Por outro lado, esse tipo de código é complicado de debugar e não é muito intuitivo, além de permitir que o programador faça muitas burradas sem aviso algum do compilador.

Agora vamos ver como fica o código do Monstro:

void Monstro::Evento(int tipo, void *param) 
{ 
    if(tipo == TIRO) 
    { 
        Tiro *tiro = (Tiro *)param;

        energia -= tiro->potencia(); 
    } 
}

Note que quando recebemos um evento do TIRO, simplesmente fazemos um cast do ponteiro e diminuímos a energia do monstro com base na potencia do tiro, extremamente simples.

Esse tipo de código funciona muito bem em jogos (apesar de existirem outras técnicas para se conseguir o mesmo feito, algumas melhores, outras nem tanto). Esta técnica serve para outros sistemas também, um exemplo é a API do Windows que usa essa técnica para fazer a comunicação entre os componentes da GUI.

No próximo tutorial, vamos ver como usar os operadores de casting do C++.


Comentários (2)
  • Almira
    avatar

    como criar um programa usando as funcoes double func(int* arr, int length);
    double func(double * arr, int length);
    onde a função retorna a média dos elementos da sequência entre os elementos mínimos e máximos.

  • Vinícius Godoy de Mendonça
    avatar

    Percorra a lista arr usando a instrução for, e então calcule o que foi pedido. Lembre-se que os índices da lista vão de 0 até length-1.

Escrever um comentário
Your Contact Details:
Gravatar enabled
Comentário:
[b] [i] [u] [url] [quote] [code] [img]   
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
:X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
Security
Por favor coloque o código anti-spam que você lê na imagem.
LAST_UPDATED2  

Busca

Linguagens

Twitter