A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

Perspectivas baseadas em procedimentos e orientadas por objectos Conceitos principais: Encapsulamento, Herança, Polimorfismo (Encapsulation, Hierarchy,

Apresentações semelhantes


Apresentação em tema: "Perspectivas baseadas em procedimentos e orientadas por objectos Conceitos principais: Encapsulamento, Herança, Polimorfismo (Encapsulation, Hierarchy,"— Transcrição da apresentação:

1 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 paradigmas de programação que suportam Geralmente uma linguagem que suporta um dado paradigma de programação suporta também os paradigmas anteriores Podemos distinguir cinco gerações de linguagens, que são: 1) não estrutural (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.)

2 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 Estar apto a construir programas triviais que utilizem encapsulamento, herança e polimorfismo.

3 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ário para efectuar a computação desejada Ao longo dos anos o ênfase dado à escrita de programas passou do desenvolvimento de procedimentos para a organização dos dados. Ao conjunto de procedimentos que se relacionam com os dados que manipulam é normalmente chamado um módulo De notar, que nós já consideramos como o módulo pode ser organizado e construído

4 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ódulo de 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.

5 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 realmente com o resto do programa Não há maneira de o adaptar a novas situações excepto se modificarmos a sua definição

6 Consideremos a definição de um tipo de forma para usar em 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{... };

7 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 a excepção da acessibilidade atribuída aos seus membros. Na ausência de classificador de acesso, os membros de uma struct são públicos e 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 ser usadas como membros da classe shape

8 x y enum kind { circle, triangle, square }; x y Podemos definir uma forma desta maneira:

9 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

10 void shape::draw() { switch (k){ case circle: // desenho a // circunferência break; case triangle: // desenho o triângulo break; case square: // desenho o quadrado break; }; A função draw() pode ser definida da seguinte forma: O tipo de shape que para nosso exemplo pode ser: circunferência triângulo quadrado

11 Problema: Não podem adicionar uma nova forma ao sistema se não tiverem acesso ao código fonte de cada operação Não existe distinção entre as propriedades gerais de qualquer forma (a forma tem uma cor, pode ser desenhada, etc.) e as 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 expressar esta distinção e tirar partido dela Uma linguagem com construções que permitam expressar e usar esta 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

12 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 }; virtual 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 numa classe derivada desta"

13 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

14 class circle : public shape{ int radius; public: void draw() {... }; void rotate(int) {... } }; Neste caso a circunferência (circle) tem os componentos geral tã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 classe circle é derivada da classe shape. A classe shape chama-se a classe base e a classe circle chama-se a classe derivada : : : class circle public shape class circle public shape O seguinte fragmento apresenta as regras formais para a declaração da herança: A classe derivada A classe base O atributo da classe base (vai ser considerado posteriormente)

15 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 sistema não é um processo trivial

16 Vamos abordar o exemplo Assumimos que se pretende declarar uma classe que 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 monitor Nós precisamos de usar esta função mas ela não é significativa neste contexto e por isso vai ser considerada posteriormente Este é o construtor Este é o destrutor

17 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 Idade Nome A classe aluno foi derivada da classe pessoa Consideremos o seguinte programa constituído por duas funções main e print_all:

18 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 objectos da classe pessoa Quanto é uma constante que, por exemplo, pode ser definida da seguinte forma: #define Quanto 3 4. O primeiro ponteiro muito[0] vai ser preenchido com o 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 considerada na próxima página

19 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; }

20 void print_all(pessoa** ponteiro_para_pessoa, int size) {int i=0; while (i print_me(); } } ** significa ponteiro_para_pessoa Array de ponteiros ponteiro0 ponteiro1 ponteiro2 etc. objecto do tipo pessoa Podem ser dos objectos derivados da classe pessoa

21 void print_all(pessoa** ponteiro_para_pessoa, int size) {int i=0; while (i print_me(); } } Esta função imprime os dados para todos os objectos que podem ser 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 ou um ponteiro para pessoa ou um ponteiro para aluno que é o tipo derivado do tipo pessoa

22 Vocês devem compreender que: 1. A variável ponteiro_para_pessoa[índice] é um nome que pode possuir valores diferentes. 2. Se ponteiro_para_pessoa[índice] tem um valor do ponteiro para o objecto do tipo pessoa, ele permite aceder a este objecto na memória, por exemplo: memória O objecto do tipo pessoa Nome Idade

23 3. Se ponteiro_para_pessoa tem um valor do ponteiro para o objecto do tipo aluno (que foi derivado da pessoa), ele permite o acesso a este objecto na memória, por exemplo: Nome Idade grupo Memória O objecto do tipo aluno

24 void print_all(pessoa** ponteiro_para_pessoa, int size) {int i=0; while (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;

25 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 - 25 Nome - Ricardo; Idade - 21 grupo 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);

26 Podemos concluir o seguinte: 1. A função print_all pode ser usada para a visualização no monitor dos dados de qualquer objecto da classe pessoa (ou da classe derivada da classa pessoa, por exemplo da classe aluno). 2. Se declaramos qualquer nova classe derivada da classe pessoa (por exemplo empregado) podemos imprimir os respectivos dados sem redefinição da função print_all. 3. Isto permite expandir as possibilidades do programa sem redefinição do seu código. Por outras palavras, podemos usar o mesmo código mesmo em tarefas novas. 4. Isto é impossível sem a utilização de herança.


Carregar ppt "Perspectivas baseadas em procedimentos e orientadas por objectos Conceitos principais: Encapsulamento, Herança, Polimorfismo (Encapsulation, Hierarchy,"

Apresentações semelhantes


Anúncios Google