Programação em C++ - web.fe.up.pttavares/ensino/VISCI/Downloads/Apontamentos/...Visualização...

Post on 21-May-2018

233 views 4 download

Transcript of Programação em C++ - web.fe.up.pttavares/ensino/VISCI/Downloads/Apontamentos/...Visualização...

Visualização Científica

Programação em C++: Tipo de dados: simples, arrays e classes

J. BarbosaJ. Tavares

Visualização Científica

Arrays• C++ tem tipos de dados simples como short, int, float, char, double etc.

• As instruções int a;float b[10];double c[5][5];

define um inteiro simples a, um array unidimensional de floats b: b[0] .. b[9] e um array bidimensional de doubles c: c[0][0] .. c[4][4] (Importante: todos os índices de arrays começam em 0).

• Ambas a,b e c são alocadas implicitamente e serão apagadas da memória assim que terminar a função onde são declaradas. O espaço ocupado é fixado na compilação do programa.

Visualização Científica

Arrays

• Alocação dinâmica de memória permite a criação de arrays cujo dimensão é determinada durante a execução. (e.g. ler uma imagem cujo tamanho é variável.

• Importante para fazer uma gestão eficiente da memória.

• Problema: a alocação dinâmica é a principal fonte de erros nos programas em C++. Os principais motivos são:1. Aceder/Apagar arrays antes de os criar ou inicializar.2. Aceder/Apagar arrays depois de já terem sido apagados.3. Não apagar a memória reservada (alocada). Origina crash ao

fim de algum tempo de funcionamento.

Visualização Científica

Apontadores

• A alocação dinâmica de memória reserva uma quantidade de memória para uma variável, estrutura ou objecto.

• O processo de alocação retorna um endereço físico de memória para o tipo de variável/estrutura que se pretende guardar. O endereço é guardado num APONTADOR.

• O tipo de apontador indica o tipo de dados que são guardados.

• float* representa um apontador para um bloco de memória de um array de floats (os array podem ter tamanho 1 ou mais)

Visualização Científica

Alocação/libertação de memória• A alocação de memória é efectuada pelo comando new, exemplo:

int* a=new int;

a não é um inteiro. É um apontador que contém o endereço onde estáguardado um valor do tipo int.

Para aceder ao valor guardado na posição de memória apontada por a usa-se o operador *, por exemplo:

*a = 1;

• Quando o valor inteiro já não é necessário pode ser libertada a memória reservada através do comando delete, exemplo:

delete a;

Visualização Científica

Alocação/libertação de arrays

• A alocação é efectuada pelo comendo new:

int* a=new int[10]; // array de 10 inteiros

• a é um apontador que contém o endereço do primeiro elemento do array. Para aceder aos vários elementos:

a[0] = 1 ; a[3]=2; etc.

• Quando o array já não é necessário pode ser libertada a memória reservada com o comando delete []:

delete [] a;

• Atenção à diferença entre delete e delete [].

Visualização Científica

Arrays bidimensionais• Os arrays bidimensionais são criados como um array de arrays: e.g.

uma matriz 5x10 pode ser guardada como:

// aloca 5 linhas de floatsfloat** matrix=new float*[5]; for { int i=0;i<=4;i++ } {

matrix[i]=new float[10];}

float** matrix é um apontador para um array de apontadores do tipofloat*.

• O array pode ser acedido da mesma forma, e.g. matrix[0][2]=2.0

• A libertação de memória é mais complexa:

int ifor {i=0;i<=4;i++ } {

delete [] matrix[i];}delete [] matrix;

Visualização Científica

Arrays bidimensionais

• Um array bidimensional pode ser simulado por um array unidimensional, por exemplo:

float* matrix=new float[5*10]; Matrix[10*row+column]=5;

• Acesso aos dados mais trabalhoso.

• Todos os arrays usados internamente no VTK são guardados como arrays unidimensionais.

Visualização Científica

Estrutura// definir estruturastruct computador{

char processador[20];unsigned int memoria;unsigned int disco;char monitor[10];bool dvd;

};// definir variável e aceder aos elementosstruct computador c;c.memoria = 450;strcpy(c.processador, “Pentium 4”);

IMPORTANTE: não é necessário libertar a memória reservada para a estrutura. O próprio programa quando termina liberta o espaço das variáveis criadas.

Visualização Científica

Estrutura// definir apontador para estruturastruct computador *p;

// reservar espaço para a estrutura*p = new struct computador;

// aceder aos elementosp->memoria = 450;strcpy(p->processador, “Pentium 4”);

// apagar estrutura da memóriadelete p;

Visualização Científica

Programação Procedimental vs Orientada a Objectos

• Programação Procedimental

• Ainda é o paradigma de programação mais utilizado

• Maior ênfase no processamento e nos algoritmos

• Principais características: passagem de argumentos a funções e retorno de valores das funções

• Possibilidade de encapsulamento de algoritmos e separação entre interface e implementação

Visualização Científica

Interface vs ImplementaçãoInterface -- Utility.h

#include <math.h>float deg2rad(float t);

Implementação -- Utility.cpp

#inlude “utility.h”float deg2rad(float t){

return t*PI/180.0;}

• Com esta separação, podemos posteriormente alterar a implementação da função deg2rad() sem afectar os programas que chamem a função. O algoritmo para passar de graus a radianos estáencapsulado na implementação de deg2rad.

Visualização Científica

Programação Orientada a Objectos (OO)

• O encapsulamento vai mais longe, i.e. podemos definir um conjunto de variáveis relacionadas (objecto), e associar funções que operem sobre essas variáveis.

• Os dados próprios (variáveis) do objecto não são “visíveis” para o exterior, sendo apenas acessíveis através de funções definidas para esse efeito.

• O desenvolvimento de um programa consiste na criação dos objectos apropriados e da definição da sua interacção.

Visualização Científica

Especificação de Classes

As Classes consistem em:

1. Membros (variáveis) -- como as estruturas em C2. Métodos – funções especiais que estão embebidas

na classes

Quer os membros quer os métodos podem ser declarados como public ficando acessíveis ao exterior, ou private/protected o que as torna inacessíveis fora dos métodos da classe.

Visualização Científica

Exemploclass formaGeometrica{

private:float area;char nome[20];int cor;

public:formaGeometrica(void){}~formaGeometrica(void){}void setNome(char *nome){ strcpy(nome, nome); // ambiguidade}

}

strcpy(this->nome, nome);

Visualização Científica

Especificação de ClassesMétodos típicos:

1. Um ou mais métodos construtores. [Existe um por defeito].

2. Um destrutor para libertar a memória e destruir a classe. [Existe um por defeito].

3. Métodos para aceder/modificar as variáveis membro.

4. Métodos para efectuar processamento adicional.

5. Sobreposição de Operador para mudar o comportamento por defeito de certos operadores como = + -

Exemplo:Se tivermos uma classe matriz e a,b,c forem do tipo matriz*a=b+c apenas somará os endereços. =,+ podem ser redefinidos para

fazer com que a=b+c seja uma adição de matrizes.

Visualização Científica

Exemplo: Classe image

//A interfaceclass pimage {

public:// construtor – chamado quando cria um objectopimage(int width,int height);// destrutor – chamado a partir do comando delete~pimage();

// funções para aceder aos dadosfloat getPixel(int i,int j);void setPixel(int i,int j,float v);

protected:// inacessível de fora da classeint getindex(int i,int j);float* pixel_array;int dimensions[2];

};

Visualização Científica

Utilização da classe pimage

pimage* an_image = new pimage(100,100);

pimage->setPixel(10,10,3.0);float a=pimage->getPixel(10,10);

delete pimage;

Notas:1. A forma como pimage está implementada internamente não tem

interesse para o utilizador porque apenas pode alterar os dados através dos métodos getvoxel/setvoxel. A implementação pode ser alterada sem que isso altere o código acima.

2. Um objecto do tipo pimage é alocado e libertado da memória como qualquer outro tipo de variável (estrutrura), mas:• O construtor é chamado na criação do objecto (new)• O destrutor é chamado quando delete é invocado.

Visualização Científica

Implementação (1)

// construtorpimage::pimage(int width,int height){

dimensions[0]=width;dimensions[1]=height;

int tsize=dimensions[0]*dimensions[1];voxel_array=new float[tsize];for (int i=0;i<tsize;i++)

pixel_array[i]=0.0;}

pimage::~pimage(){

delete [] pixel_array;}

Visualização Científica

Implementação (2)

// funções de acessofloat pimage::getpixel(int i,int j){

int index=getindex(i,j); return pixel_array[index];

}

void pimage::setvoxel(int i,int j,float v){

int index=getindex(i,j); pixel_array[index]=v;

}

// função interna, getindex()int pimage::getindex(int i,int j){

return i*dimensions[1]+j;}

Visualização Científica

Extensão de pimage//A interfaceclass p2image : public pimage {

public: // funcionalidade adicionada. Se não for passado// parâmetro considera valor 0.0void fill(float a=0.0);

};

//Implementaçãovoid p2image::fill(float a){ int size, i;

size=dimensions[0]*dimensions[1];for (i=0;i<tsize;i++)

pixel_array[i]=a;}

p2image deriva de pimage. Herda toda a funcionalidade de pimage e adiciona o método fill().

Enquanto que à priori podemos supor que esta função poderia ser definida também em

pimage, isto nem sempre é possível especialmente se o código fonte de pimage não estiver

disponível (i.e. pimage poderia ser parte de uma biblioteca de classes pré-compilada como é

o caso do VTK).

Visualização Científica

Herança

• No exemplo anterior vimos como obter novas classes a partir de outras já existentes.

• Classes derivadas podem reescrever métodos da classe principal.

• Podemos definir métodos virtuais (virtual) que especificam uma interface e deixar a implementação para as classes derivadas.

• A combinação de múltiplos camadas de classes derivadas resulta numa hierarquia de classes.

Visualização Científica

Exemplo de hierarquia de classes escrita de imagens em ficheiro

BaseImageWriter

TIFFImageWriter JPEGImageWriter

Visualização Científica

BaseImageWriter

Classe abstracta principal que fornece a funcionalidade comum à tarefa de escrita para ficheiro.// Interfaceclass BaseImageWriter {

public:// Construtor e DestrutorBaseImageWriter();virtual ~BaseImageWriter();

// Métodosvirtual void SetInput(vtkImageData* input);virtual void SetFilename(char* filename); virtual void Write()=0; // “pure virtual“

// Write não tem implementação// porque depende do tipo de ficheiro usado

protected: // variáveis internaschar* Filename;Data* Input;

};

Visualização Científica

TIFFImageWriter e JPEGImageWriter

Derivadas de BaseImageWriter e fornecem implementações concretas para Write():

class TIFFImageWriter : public BaseImageWriter {public:virtual void Write();

};

void TIFFImageWriter::Write() {// código para gravar no formato TIFF

}

class JPEGImageWriter : public BaseImageWriter {public:virtual void Write();

};

void JPEGImageWriter::Write() {// código para gravar no formato JPEG

}

Visualização Científica

Adicionar maior funcionalidade a JPEGImageWriter

Podemos re-escrever o método SetFilename para garantir a extensão .jpeg

class JPEGImageWriter : public BaseImageWriter {public:virtual void SetFilename(char* filename); virtual void Write();virtual void SetJPEGCompression(float c);

protected:float compression;

};

void JPEGImageWriter::SetJPEGCompression(float c) {compression=c;

}void JPEGImageWriter::SetFilename(char* filename) {

// Verifica de o nome acaba com .jpeg// senão retira extenção e adiciona .jpeg// Escreve em ficheiro

}

Enquanto que TIFFImageWriter aceita a implementação por defeito de SetFilename de BaseImageWriter, JPEGImageWriter modifica o método para obter um objectivo

particular.

Visualização Científica

Polimorfismo

• Considere a função:

void SaveImage(Data* image,BaseImageWriter* writer,char* filename)

{writer->SetFilename(filename);writer->SetInput(image);writer->Write();

}

O argumento writer é especificado como sendo do tipo BaseImageWriter. Resulta assim num apontador quer para TIFFImageWriter ou JPEGImageWriter, sendo possível passar qualquer um deles. Qualquer método que não esteja definido em BaseImageWriter (e.g. SetJPEGCompressionFactor) não estão disponíveis através de writeruma vez que SaveImage não sabe da sua existência.

Visualização Científica

Comentários finais

• As classes principais definem interfaces comuns para as classesderivadas.

• As classes principais podem fornecer implementações para alguns dos métodos da interface.

• Métodos que são para ser reescritos pela classe derivada são designados pelo comando virtual.

• Métodos para os quais a classe principal fornece apenas a interface e não uma implementação são designados de pure virtual. Tem de ser implementados obrigatoriamente nas classes derivadas.

• Classes que contêm apenas métodos pure virtual não podem ser instanciadas, i.e. criar objectos desse tipo. Apenas as classes que derivem destas podem ser instanciadas.