Introdução ao Paradigma de Programação por Objectos 1. Tipos abstractos (Abstract types) 2. Conceitos principais: Encapsulamento, Herança, Polimorfismo (Encapsulation, Hierarchy, Polymorphism) 3.1. Classes e Objectos; 3.2. Métodos e Dados; 3.3. Interfaces e Implementação; 3.4. Programação por contracto; 3.5. Gestão de memória O que é importante: 1. Perceber o que é um tipo abstracto. 2. Perceber o que é um encapsulamento. 3. Perceber o que é uma classe. 4. Perceber a diferença entre classes e objectos. 5. Perceber as regras da linguagem C++ que são aplicadas para declarar classes e definir objectos dessas classes 6. Estar apto a construir programas triviais que utilizem classes. Em C podemos definir: int a,b,c; e podemos usar as operações: +, -, *, etc ? Será que podemos definir um tipo novo, por exemplo? complex a,b,c; Podemos definir as nossas operações para o nosso tipo, por exemplo? ? +, -, *, etc Tipos abstractos (Abstract types) Memória nomes dos dados int a,b,c; 3 4 7 a b c atribuição simples a = 3; b=4 c = a + b; o tipo de dados atribuição simples adição c = a # b; indefinido Tipos abstractos (Abstract types) Memória nomes dos dados string a,b,c; Av eiro Aveiro a b c atribuição simples a = “Av”; b = “eiro” c = a + b; o tipo de dados adição atribuição simples c = a # b; indefinido SET O tipo novo a1 a3 Por exemplo: SET A={a1,a2,a3}; SET B={a3,a4,a5}; SET C; C = A + B; a2 a4 a3 a5 + a1 a2 a3 a4 a5 = a1, a2, a3, a4, a5 - C = A - B; a1 a2 a3 a3 a4 a5 = a1, a2 Um paradigma, designado Abstracção de tipos de dados, consiste em 1. definir os tipos de dados de que se tenha necessidade e 2. criar para cada um deles um conjunto completo de operações a1,a2,a3,etc. SET + * A+B A*B A-B A abstracção de dados assume a definição de tipos de dados abstractos, ou por outras palavras, alguns tipos de dados introduzidos pelo utilizador, isto é, definido por ele. nomes dos dados pessoa p1,p2,p3; os atributos: o tipo de dados 1. Nome. 2. Morada. 3. Idade. as operações: &, ==, !=, etc. Onde mora? cout << &p1; Ílhavo A abstracção de dados requer que consideremos as operações sobre os dados e as implementações dessas operações Temos as seguintes operações: &, ==, !=, etc. Vamos assumir que estas operações já foram implementadas e, por isso, elas podem ser usadas em programas, por exemplo: pessoa p1,p2,p3; // definição das três pessoas p1, p2 e p3 if (p1 == p2) // fazer alguma coisa, por exemplo: cout << &p3; Por outro lado, se alguma operação destas não existir deveremos implementá-la em C++. Suponhamos que é dado um módulo. Neste caso é suficiente conhecer as operações que efectua mas não é necessário conhecer detalhes, i.e. como essas operações são efectuadas O conceito de tipos abstractos é um conceito chave da programação Isto assume a separação e consideração independente de duas noções básicas, que são a implementação e a interface A modularidade e a abstracção complementam-se A modularidade assume que escondemos os detalhes pessoa da implementação numa caixa preta ? A abstracção permite especificar em particular &,cada ==, !=,módulo etc. antes de escrever o programa ? &, ==, !=, etc. Podem-se distinguir dois tipos de abstracção que são: • a abstracção ao nível dos procedimentos e • a abstracção dos dados A primeira requer uma consideração à parte. O objectivo de um procedimento é a sua implementação interna. Nós assumimos que um procedimento é algo como uma função em C ou C++ A abstracção dos dados requer que consideremos as operações sobre os dados e as implementações dessas operações a b void swap(int* a,int* b) { int temp; temp=*a; &,==, &, ==, !=, etc. Pessoa *a = *b; !=,etc. } *b=temp; swap Suponhamos que é dado um módulo. Neste caso é suficiente conhecer as operações que efectua mas não é necessário conhecer detalhes, i.e. como essas operações são efectuadas Assim, uma abstracção permite separar uma representação externa de um módulo da sua estrutura interna A abstracção de dados assume a definição e consideração de tipos de dados abstractos, ou por outras palavras, alguns tipos de dados introduzidos pelo utilizador, isto é, definidos por ele Vamos separar duas noções importantes que são os tipos de dados abstractos e os tipos abstractos A primeira noção já foi especificada A segunda noção é considerada um tipo de dados abstracto juntamente com o novo conjunto de funções e operações que foi definido para este tipo de dados abstracto dados funções operações Quando digo função quero dizer qualquer função em linguagem C/C++, como por exemplo main, função_definida_por_mim, sqrt (raiz quadrada), etc Quando digo operação quero dizer qualquer operação pré-definida ou definida pelo utilizador, como por exemplo soma (+), subtracção (-), divisão (/), etc. As operações definidas pelo utilizador são muito úteis para os tipos de dados definidos pelo utilizador. Permitem redefinir operações já definidas na linguagem para tipos de dados definidos pelo utilizador memória int a=10; b é o endereço de a na memória int* b=&a; pessoa p=“Paulo”; cout << &p; Ílhavo, 3830 Rua Coutada, 56 10 Paulo Vamos criar o tipo “pessoa” em C++: Por exemplo, podemos usar uma estrutura que é qualquer coisa do tipo: struct pessoa { unsigned short Idade; char* Morada; char* Nome; }; De notar que a nossa especificação da estrutura pessoa (do tipo abstracto pessoa) ainda não está completa Os valores dos campos que são Nome, Morada e Idade não estão definidos Por outras palavras, os campos devem, em geral, ser inicializados Para este efeito podemos usar algumas funções específicas no contexto da estrutura Isto é possível na linguagem C++ e não é possível na linguagem C Inicialização significa atribuir valores iniciais aos atributos do objecto Este trabalho é efectuado por uma função especial que é chamada construtor da classe O construtor struct pessoa da classe possui o mesmo nome que a classe e não nenhum valor, nem do tipo void. Todas as outras { devolve unsigned short Idade; características de um construtor de uma classe são semelhantes char* Morada; char* Nome;às de um método normal pessoa(unsigned short Id, char* instala Mor, char* No)para Para o nosso exemplo o construtor valores { três atributos Idade = Id; que são Nome, Morada e Idade strcpy(Morada,Mor); Esta função (ou este método) strcpy(Nome,No); } pode ser construída da seguinte maneira }; Agora podemos usar o tipo pessoa da seguinte maneira: pessoa p(25,”Ílhavo”,”Ricardo”); struct pessoa { unsigned short Idade; char* Morada; char* Nome; pessoa(unsigned short Id, char* Mor, char* No) { Idade = Id; Morada=Mor; Nome=No; } }; Agora podemos usar o tipo pessoa da seguinta maneira: pessoa p(25,”Ílhavo”,”Ricardo”); Agora temos: p.Idade=25, p.Morada=“Ílhavo”, p.Nome=“Ricardo”