View
110
Download
2
Category
Preview:
Citation preview
Perspectivas baseadas em procedimentos e orientadas por objectos
Conceitos principais: Encapsulamento, Herança, Polimorfismo
(Encapsulation, Hierarchy, Polymorphism)
A evolução das linguagens pode analisar-se através dos paradigmasde programação que suportam
Geralmente uma linguagem que suporta um dado paradigma deprogramação suporta também os paradigmas anteriores
Podemos distinguir cinco gerações de linguagens, que são: 1) nãoestrutural (Cobol, Fortran, Basic); 2) procedimental (C, Pascal);
3) modular (Modula II); 4) com abstracção de tipos de dados(ADA); 5) programação orientada por objectos (C++, JAVA, etc.)
O que é importante:
1. Perceber o que são paradigmas de programação.
2. Perceber mais detalhes sobre encapsulamento.
3. Perceber o que é herança.
4. Perceber o que é um polimorfismo.
5. Perceber as regras da linguagem C++.
6. Estar apto a construir programas triviais que utilizem encapsulamento, herança e polimorfismo.
O paradigma da programação procedimental é:
Decidir quais os procedimentos que se pretendem:usar os melhores algoritmos que se puder encontrar
Devemos-nos concentrar no processamento que é necessáriopara efectuar a computação desejada
Ao longo dos anos o ênfase dado à escrita de programas passoudo desenvolvimento de procedimentos para a organização
dos dados. Ao conjunto de procedimentos que se relacionamcom os dados que manipulam é normalmente
chamado um módulo
De notar, que nós já consideramos como o módulo pode serorganizado e construído
O paradigma de programação modular passa a ser:
Decidir quais os módulos que se pretendem:dividir o programa para esconder os dados em módulos
Agora a técnica para escrever "bons procedimentos”aplica-se a cada procedimento num módulo
O exemplo mais comum da definição de um módulo é um módulode stack
Os principais problemas que têm de ser resolvidos são:
1. Fornecer ao utilizador um interface para o stack (por exemplo funções push() e pop()).
2. Assegurar que a representação do stack (por exemplo, um array de elementos) possa ser acedida apenas a partir dessa interface.
3. Assegurar que o stack é inicializado antes de ser usado pela primeira vez.
O paradigma de programação para tipos abstractos é:
Decidir quais os tipos que se pretendem:fornecer um conjunto de operações para cada tipo
Problemas com a abstracção de dados
Um tipo abstracto define um tipo de caixa preta
Uma vez definido ele não interage realmentecom o resto do programa
Não há maneira de o adaptar a novas situações exceptose modificarmos a sua definição
Consideremos a definição de um tipo de forma para usarem sistemas gráficos. Vamos assumir por enquanto que
o sistema deve suportar circunferências, triângulos e quadrados.Vamos assumir também que temos:
class point { ... };class color { ... };
class point { public:
int x:int y; };
x
y
class color {int col;
};
class point { ... };class color { ... };
De notar que as palavras chave struct e class são equivalentes, com aexcepção da acessibilidade atribuída aos seus membros. Na ausência
de classificador de acesso, os membros de uma struct são públicose os de uma class são privados.
Por isso as seguintes definições do tipo point são equivalentes:
struct point { int x:int y; };
public:
As classes point e color vão serusadas como membros da
classe shape
x
yenum kind { circle, triangle, square };
x
y
Podemos definir uma forma desta maneira:
class shape {point center;color col;kind k;// representação de “shape”
public:point where() { return center; }void move(point to) { center = to; draw(); }void draw();void rotate();// mais operações
};
x
y
x
y
void shape::draw(){
switch (k) {case circle:
// desenho a // circunferênciabreak;
case triangle: // desenho o triângulo break;
case square: // desenho o quadradobreak;
};
A função draw() pode ser definida da seguinte forma:
O tipo de shape
que para nossoexemplo pode ser:
circunferência
triângulo
quadrado
Problema: Não podem adicionar uma nova forma ao sistema senão tiverem acesso ao código fonte de cada operação
Não existe distinção entre as propriedades geraisde qualquer forma (a forma tem uma cor, pode ser desenhada, etc.) eas propriedades de uma forma específica (uma circunferência possui
raio, é desenhada por uma função própria para esse efeito, etc.)
A programação orientada por objectos define-se por expressaresta distinção e tirar partido dela
Uma linguagem com construções que permitam expressar e usaresta distinção suportam a programação orientada por objectos.
As outras linguagens não.
O mecanismo de herança do C++ (herdado da Simula)fornece a solução
Primeiro vamos declarar uma classe que vai definir as propriedades gerais para todos os shapes:
class shape {point center;color col;// representação de “shape”
public:point where() { return center; }void move(point to) { center = to; draw(); }virtual void draw();virtual void rotate();// mais operações
};
virtualvirtual
As funções cujo interface pode ser definido mas que a implementaçãoé específica de cada forma foram marcadas com a palavra chave
"virtual". Isto significa que "pode ser redefinida mais tarde numaclasse derivada desta"
void rotate_all(shape v[], int size, int angle)//A função gira todos os membros de array “v” // (de tamanho “size”) em “angle” graus{
int i=0;while (i<size) {
v[i].rotate(angle);i+=1;
}}
Para esta definição podemos escrever a função geral que vaimanipular os shapes:
Para definir o shape concreto nós devemos dizer que isto é o shapee indicar das propriedades concretas para este shape (incluindo
as funções virtuais), por exemplo:
class circle : public shape {int radius;
public:void draw() { ... };void rotate(int) { ... }
};
Neste caso a circunferência (circle) tem os componentos geraltãis como:
private:point center;color col;
public:point where(); void move(point to);
e os componentos especial tãis como:
private:int radius;
public:void draw() { ... };void rotate(int) { ... }
Neste exemplo a classecircle é derivada da classe
shape. A classe shapechama-se a classe base
e a classe circle chama-sea classe derivada
:
: :
class circle public shape
class circle public shape
O seguinte fragmento apresenta as regras formaispara a declaração da herança:
A classe derivada A classe base
O atributo da classe base (vai ser considerado posteriormente)
O paradigma de programação é:
Decidir quais são as classes que pretendemos;fornecer um conjunto de operações para cada classe;explicitar os aspectos comuns através da utilização de herança
Quando não existem aspectos comuns a abstracção de dados ésuficiente
Encontrar aspectos comuns entre os vários tipos num sistemanão é um processo trivial
Vamos abordar o exemplo
Assumimos que se pretende declarar uma classeque se chama “pessoa” (de notar que uma classe semelhante foi
considerada na aula anterior):
class pessoa { unsigned short Idade;
char* Nome;public:
pessoa(unsigned short Id=0, char* No="");virtual ~pessoa();virtual void print_me();const pessoa& operator =(const pessoa&);
};
Esta funçãao será utilizada para a visualização dos dados no monitorNós precisamos de usar esta função mas ela não é significativa
neste contexto e por isso vai ser consideradaposteriormente
Este é o construtor
Este é o destrutor
Vamos assumir que nós criámos uma classe derivada da classe pessoa,por exemplo:
class aluno : public pessoa { int grupo;public:
void print_me();aluno(unsigned short, char*, int);virtual ~aluno();const aluno& operator =(const aluno& pes);
};
Este é o número do grupo do aluno
Esta função será utilizada para a visualização dos dados no monitor
IdadeNome
A classe aluno foi derivada da classe pessoa
Consideremos o seguinte programa constituído por duas funçõesmain e print_all:
int main(int argc, char* argv[]){ pessoa p(25,"Paulo");
aluno a(21, "Ricardo", 6251);p.print_me();pessoa* muito[Quanto];muito[0]=&p;muito[1]=&a;print_all(muito,Quanto);return 0;
}
O programa main realiza das seguintes acções:1. Declara dois objectos, que são p do tipo pessoa e a do tipo aluno;
2. Executa a função p.print_me para o objecto p;
3. Declara array que é composto por ponteiros para objectosda classe pessoa
“Quanto” é umaconstante que, por exemplo,pode ser definida da seguinte forma:#define Quanto 3
4. O primeiro ponteiro “muito[0]” vai ser preenchido como valor &p que é o ponteiro para o objecto “p” do tipo pessoa.
5. O segundo ponteiro “muito[1]” vai ser preenchido com o valor &a que é o ponteiro para o objecto “a” do tipo aluno.
6. Executa a função print_all que vai ser consideradana próxima página
int main(int argc, char* argv[]){ pessoa p(25,"Paulo");
aluno a(21, "Ricardo", 6251);p.print_me();pessoa* muito[Quanto];muito[0]=&p;muito[1]=&a;print_all(muito,Quanto);return 0;
}
void print_all(pessoa** ponteiro_para_pessoa, int size){ int i=0;
while (i<size) { ponteiro_para_pessoa[i++]->print_me(); }}
** significa
ponteiro_para_pessoa
Array de ponteiros
ponteiro0
ponteiro1
ponteiro2
etc.
objecto do tipo pessoa
objecto do tipo pessoa
objecto do tipo pessoa
objecto do tipo pessoa
Podem ser dosobjectos derivados
da classe pessoa
void print_all(pessoa** ponteiro_para_pessoa, int size){ int i=0;
while (i<size) { ponteiro_para_pessoa[i++]->print_me(); }}
Esta função imprime os dados para todos os objectos que podemser determinados através dos respectivos ponteiros.
Idade,Nome
Idade,Nome,grupo
Isto pode ser feito com a ajuda da função print_me
Já vimos que o ponteiro_para_pessoa pode ser ouum ponteiro para pessoa ou um ponteiro para aluno que é o tipo
derivado do tipo pessoa
Vocês devem compreender que:
1. A variável ponteiro_para_pessoa[índice] é um nome que podepossuir valores diferentes.
2. Se ponteiro_para_pessoa[índice] tem um valor do ponteiro para oobjecto do tipo pessoa, ele permite aceder a este objecto na memória,por exemplo:
memória
O objectodo tipo pessoa
NomeIdade
3. Se ponteiro_para_pessoa tem um valor do ponteiro para o objectodo tipo aluno (que foi derivado da pessoa), ele permite o acessoa este objecto na memória, por exemplo:
NomeIdadegrupo
Memória
O objectodo tipo aluno
void print_all(pessoa** ponteiro_para_pessoa, int size){ int i=0;
while (i<size) { ponteiro_para_pessoa[i++]->print_me(); }}
Idade,Nome
Idade,Nome,grupo
pessoa p(25,"Paulo");aluno a(21, "Ricardo", 6251);p.print_me();pessoa* muito[Quanto];muito[0]=&p;muito[1]=&a;print_all(muito,Quanto);
Vamos abordar o seguinte código: 1. i=0; 2. ponteiro_para_pessoa[0];
3. print_me() para pessoa;
5. ponteiro_para_pessoa[1];
4. i=1;
6. print_me() para aluno;
Agora vamos considerar a função print_me:
void pessoa::print_me(){ cout << "Nome - " << Nome << "; Idade - " << Idade << endl;}
void aluno::print_me(){ pessoa::print_me(); cout << "grupo - " << grupo << endl;}
Nome - Paulo; Idade - 25Nome - Ricardo; Idade - 21grupo - 6251
Nome = “Paulo”
Idade = 25
saltar na próxima linha
void pessoa::print_me(){ cout << "Nome - " << Nome << "; Idade - " << Idade << endl;}
grupo = 6251
Nome - Ricardo; Idade - 21
pessoa p(25,"Paulo");aluno a(21, "Ricardo", 6251);
Podemos concluir o seguinte:
1. A função print_all pode ser usada para a visualização nomonitor dos dados de qualquer objecto da classe pessoa (ouda classe derivada da classa pessoa, por exemplo da classe aluno).
2. Se declaramos qualquer nova classe derivada da classepessoa (por exemplo empregado) podemos imprimir os respectivosdados sem redefinição da função print_all.
3. Isto permite expandir as possibilidades do programa semredefinição do seu código. Por outras palavras, podemos usaro mesmo código mesmo em tarefas novas .
4. Isto é impossível sem a utilização de herança.
Recommended