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

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

Programação Orientada aos Objectos Paulo Marques Departamento de Eng. Informática Universidade de Coimbra Out/2005 Core C++: Uma abordagem.

Apresentações semelhantes


Apresentação em tema: "Programação Orientada aos Objectos Paulo Marques Departamento de Eng. Informática Universidade de Coimbra Out/2005 Core C++: Uma abordagem."— Transcrição da apresentação:

1 Programação Orientada aos Objectos Paulo Marques Departamento de Eng. Informática Universidade de Coimbra pmarques@dei.uc.pt Out/2005 Core C++: Uma abordagem tutorial

2 2 Sobre o que é que vamos falar? Estruturas básicas em C++ Criação Dinâmica de Objectos Construção e Destruição de Objectos Herança, Polimorfismo e Destrutores Virtuais Redefinição de Métodos Redefinição de Operadores Construtor Cópia e Operador Atribuição Gestão de Erros: Excepções Esta sessão continua o trabalho desenvolvido na primeira formação POO: Uma Introdução Usando C++

3 3 Tipos de Dados Básicos O tamanho de cada tipo de dados varia de plataforma para plataforma. Alguns tipos não estão especificados como sendo com ou sem sinal

4 4 Controlo de Fluxo (1)

5 5 Controlo de Fluxo (2)

6 6 Aspecto de um programa completo // Importa biblioteca e passa a usar o espaço de nomes standard #include using namespace std; // Programa principal int main() { // Declara uma tabela de inteiros de tamanho variável vector myTable; // Adiciona-lhe 10 números for (unsigned i=0; i<10; i++) myTable.push_back(i); // Imprime o seu conteúdo for (unsigned i=0; i<myTable.size(); i++) cout << myTable[i] << endl; return 0; } Inclusão de bibliotecas Uso da biblioteca standard STL: Biblioteca de estruturas de dados e algoritmos! Envio para o ecrã

7 7 A classe Pessoa da última sessão class Pessoa { private: string _nome; int _idade; public: Pessoa(string nome, int idade); void imprime(); }; Pessoa::Pessoa(string nome, int idade) { _nome = nome; _idade = idade; } void Pessoa::imprime() { cout << "[" << _nome << "/" << _idade << "]" << endl; } Pessoa.cpp Pessoa.h

8 8 Construtores, Destrutores e Alcance Ao criar-se um objecto directamente, este é criado no stack da aplicação. Trata-se de uma variável automática. O alcance da variável está limitado à função onde foi definido. O destrutor de uma classe é um método especial que é invocado sempre que um objecto deixa de existir. É responsável por limpar todos os recursos associados a esse objecto (e.g. memória, handlers gráficos). void f() { Pessoa cliente("Carlos Manuel", 30); //... cliente.imprime(); } Criação do objecto no stack (construtor chamado) Destruição automática do objecto (destrutor chamado)

9 9 Declaração do Destrutor É semelhante ao Construtor, mas colocando um ~ antes do nome da classe. Não leva parâmetros e não retorna nada Nunca é chamado explicitamente class Pessoa {... public: ~Pessoa(); }; Pessoa::~Pessoa() { // Limpa eventuais recursos associados à pessoa //... } Pessoa.cpp Pessoa.h

10 10 Alocação Dinâmica de Memória Muitas vezes é necessário ter variáveis com um alcance (scope) superior a um método Usa-se alocação dinâmica de memória. Para alocar dinamicamente um objecto, utiliza-se o operador new Retorna um ponteiro para um novo objecto do tipo pedido. O construtor do objecto é sempre invocado Não existe garbage collection, a memória tem de ser explicitamente libertada usando o operador delete! Caso tenha sido criado um array de objectos, é necessário usar o operador delete[]. O destrutor é invocado automaticamente ao usar este operador. Pessoa* cliente = new Pessoa("Carlos Manuel", 30); delete cliente;

11 11 Classe Conjunto (Multi-conjunto) Consideremos uma classe que representa um multi-conjunto de números inteiros. Pode-se adicionar números e verificar a sua presença no conjunto

12 12 A sua implementação Inicialmente tem tamanho para um elemento (os elementos são guardados no heap!) No final garante que todos os elementos são apagados!

13 13 A sua implementação (2) O conjunto cresce automaticamente sempre que não existe mais espaço!

14 14 A sua implementação (3)

15 15 Pequeno programa de teste

16 16 Executando...

17 17 Qual é a grande limitação desta classe? (Bem, uma das grandes limitações...) Apenas permite lidar com inteiros. Mas, porque não armazenar qualquer tipo de objectos??

18 18 Programação utilizando genéricos (templates) Permite criar uma família de funções, parametrizadas por um tipo de dados abstracto. Meta-programação Existe há anos em C++: a STL é baseada neles Adição recente em Java (J2SE 5.0) e.NET (2.0) Conjunto c; c.adiciona(10); c.adiciona(20); Conjunto clientes; Pessoa p(João Carlos, 20); clientes.adiciona(p);

19 19 Qual o aspecto da implementação dos métodos? Têm de incluir a definição do template... (Incluído o construtor e destrutor!)

20 20 MonoConjunto Imaginemos que queremos criar uma nova classe, derivada de Conjunto, que permite apenas ocorrências únicas dos seus elementos. Tem de ser modificado para garantir que o elemento ainda não existe.

21 21 MonoConjunto::adiciona() Mas, qual é o problema com o seguinte código? Nota: Conjunto ::adiciona(valor) chama explicitamente o método adiciona da classe Conjunto. I.e. da classe acima!

22 22 MonoConjunto::adiciona() Mas, qual é o problema com o seguinte código? Elementos privados de Conjunto!

23 23 Níveis de acesso em C++ private: Todos os elementos declarados como private apenas são acessíveis à classe em questão. Nota: também são acessíveis a elementos friend, falaremos disto mais tarde. protected: Todos os elementos declarados como protected são acessíveis à classe em questão e às classes derivadas desta. Usar com muito cuidado pois viola parcialmente o encapsulamento. Em classes pensadas para serem herdadas, considerar quais são os elementos que eventualmente serão necessários serem acedidos por outras classes/funções. public: Elementos declarados como public são acessíveis a todas as classes e funções do programa.

24 24 Nova versão de Conjunto Admitindo que o programador pensou explicitamente Conjunto para criar outras classes, é provável que definisse essa classe da seguinte forma:

25 25 virtual? Garante que case se usem ponteiros para aceder aos membros desta classe (ou derivadas) ou caso os mesmos sejam passados como parâmetros, são chamados os métodos correctos! Porquê?

26 26 virtual (2) Garante que case se usem ponteiros para aceder aos membros desta classe (ou derivadas) ou caso os mesmos sejam passados como parâmetros, são chamados os métodos correctos! Usando o exemplo de Pessoa, Empregado e Patrao da última sessão... Chama Empregado::imprime() ou Patrao::imprime() consoante o tipo real do objecto na tabela.

27 27 Porquê o destrutor virtual? Qual o resultado da execução??

28 28 Porquê o destrutor virtual? (2) O destrutor de Empregado nunca é chamado. MEMORY LEAK!!! O sistema não sabe qual o verdadeiro tipo associado ao objecto apontado por p

29 29 Com um destrutor virtual... Garante que os destrutores são sempre chamados correctamente OK!

30 30 REGRAS A SEGUIR – Classes Para Herança Devem sempre definir um destrutor virtual Mesmo nos casos em que não é necessário realizar nenhuma limpeza específica no objecto. Normalmente, a limpeza refere-se a objectos alocados dinâmicamente ou a recursos não automáticos (e.g. handlers gráficos, semáforos, etc.) Todos os métodos pensados para serem herdados, sendo modificados em classes derivadas, têm de ser virtual Todos os campos ou métodos a que eventualmente seja necessário aceder em classes derivadas devem ser protected Utilizar com cuidado! É preferível ter métodos protected do que variáveis protected: mantêm um maior encapsulamento

31 31 Conjunto Revisitado

32 32 MonoConjunto Revisitado Nota: uma vez declarados como virtual na classe base não deixam de ser virtual nas classes derivadas. No entanto, é mais elegante torná-lo explícito.

33 33 Implementação de MonoConjunto Não é necessária nenhuma inicialização nem limpeza explícita Afinal não é preciso aceder explicitamente às estruturas de dados (Óptimo!)

34 34 Nota sobre construtores e destrutores Os construtores e destrutores são sempre invocados, quer tal seja feito explicitamente na lista de inicialização, quer não. Um Empregado tem de ter todos os campos de Pessoa. Logo, antes de criar Pessoa, é necessário criar completamente Empregado. A lista de inicialização permite algum controlo sobre a construção dos objectos acima. Os objectos acima são sempre completamente construídos antes de se entrar no corpo do construtor (i.e. {... } ) A destruição é feita pela ordem inversa da construção { Empregado p; } 1 2 4 3

35 35 Nota sobre construtores e destrutores (2) À partida, todas as classes possuem um construtor por omissão, mesmo que o programador não o declare. Isto é, um construtor sem parâmetros. Por vezes é útil não deixar os objectos serem construídos dessa forma (E.g. Pattern Factory ou classes internas) É possível desligar explicitamente o construtor por omissão Caso se declare um construtor com parâmetros, o construtor por omissão é automaticamente desligado. Caso seja necessário, o programador tem de o definir explicitamente.

36 36 Redefinição de Operadores O C++ permite a redefinição de operadores Redefinir um operador é semelhante a implementar um método Seria interessante poder escrever este código!

37 37 Conjunto Revisitado

38 38 Operadores como métodos conversão

39 39 Implementação

40 40 Execução

41 41 Operadores Redefiniveis em C++ Atenção: a redefinição de operadores é algo que implica conhecer bastantes regras e é potencialmente perigoso Certos operadores são utilizados automaticamente em certas circunstâncias. Ao serem redefinidos, pode levar a problemas subtis. Por outro lado, muitas vezes é essencial redefini-los. A sintaxe é relativamente uniforme mas alguns operadores são especiais. Existem operadores que devem ser redefinidos conjuntamente (e.g. == e !=,, >=) +-*/%^&|~ !,=<><=>=++-- <<>>==!=&&||+=-=/= %=^=&=|=*=<<=>>=[]() ->->*newnew[]deletedelete[]

42 42 ostream Como enviar um conjunto para o ecrã? Basta uma redefinição global... Notas:

43 43 friends No caso anterior, Conjunto fornecia todos os métodos necessários para implementar o operador <<. Mas, o que é que acontece com Pessoa? class Pessoa { protected: string _nome; int _idade; public: Pessoa(string nome, int idade); virtual ~Pessoa(); }; Se tentarmos escrever a função que implementa o operador <<(ostream&, Pessoa& p), este não tem acesso nem a _nome nem a _idade!

44 44 friends Ao declarar uma outra classe ou função como friend, dá-se acesso a todos os campos privados da mesma! Para declarar uma outra classe com friend: friend class X; class Pessoa { protected: string _nome; int _idade; public: Pessoa(string nome, int idade); virtual ~Pessoa(); friend ostream& operator<<(ostream& os, Pessoa& p); };

45 45 friends em acção class Pessoa { protected: string _nome; int _idade; public: Pessoa(string nome, int idade); virtual ~Pessoa(); friend ostream& operator<<(ostream& os, Pessoa& p); };

46 46 Qual o resultado da execução deste código?

47 47

48 48 O problema das cópias Sempre que é necessário copiar um objecto, por omissão, a cópia é realizada elemento a elemento da classe. Isto é problemático no caso de classes que utilizem ponteiros e usem criação dinâmica de objectos! 3 4 _nElementos _capacidade _conjunto 102030 conjA 3 4 _nElementos _capacidade _conjunto conjB

49 49 Problema das cópias Quando se acrescenta um elemento ao segundo conjunto, na verdade, também se está a manipular a informação do primeiro. Quando ambos os objectos são destruídos, ambos vão tentar libertar a memória. Daí o Segmentation Fault!!! 3 4 _nElementos _capacidade _conjunto 102030 conjA 3 4 _nElementos _capacidade _conjunto conjB Não se pode apagar a mesma memória duas vezes!

50 50 Construtor de Cópia As seguintes operações correspondem à invocação do Construtor de Cópia da classe: O construtor de cópia tem como parâmetros uma referência constante para um objecto do mesmo tipo da classe. E.g. Conjunto(const Conjunto & outro) Também é chamado sempre que um objecto é passado por valor para dentro de uma função e, em certas circunstâncias, quando é retornado de uma função.

51 51 Operador de Atribuição O operador de atribuição (=) é chamado sempre que é necessário substituir um objecto já existente por um novo O destrutor do existente não é invocado!

52 52 Definição do Construtor de Cópia

53 53 Definição do operador de atribuição

54 54 Cópia de Objectos, Pontos Importantes Por omissão, todas as classes possuem um construtor de cópia e um operador de atribuição Tal como no caso do construtor por omissão, também é possível desligá-los, tornando-os privados. Sempre que é necessário criar um novo objecto a partir de outro, é chamado o construtor de cópia. Sempre que é necessário copiar um objecto para outro já existente, é invocado o operador atribuição. Em ambos os casos, o compilador gera código para que, recursivamente, copia as variáveis membro existentes usando construtores-cópia/operadores atribuição. Em geral, quando os objectos são criados dinamicamente dentro de uma classe, é necessário redefinir o construtor cópia e o operador de atribuição. SEMPRE QUE SE REDEFINE O OPERADOR CÓPIA DEVE REDEFINIR-SE O OPERADOR ATRIBUIÇÃO E VICE-VERSA Não o fazer provavelmente indica que se passa algo de muito errado!

55 55 O resultado da execução...

56 56 Excepções Tal como na maioria das linguagens modernas, o C++ encoraja a utilização de excepções para gestão de erros Ao contrário da maioria das outras linguagens, uma excepção pode ser qualquer coisa (um inteiro, um objecto, uma referência, um ponteiro, etc.) Embora seja possível declarar as excepções que um método pode lançar, não há obrigatoriedade de as apanhar. Da mesma forma, um método pode lançar excepções que não declara.

57 57 MonoConjunto revisitado Classe base recomendada para erros nas aplicações, devido ao programador! Aqui acontece a excepção e o programa é abortado!

58 58 O resultado (espectável ou não...)

59 59 Salto para o handler correspondente

60 60 Qual o aspecto do código?

61 61 Definir uma nova excepção (parametrizada )

62 62 As novas definições

63 63 Ainda resultando em... As excepções são um tópico complexo e abrangente em C++. É importante consultar uma referência antes das começar a utilizar! Isto constitui apenas uma brevíssima introdução ao tópico!

64 64 Antes de terminarmos... 1 2 2 3 1. Objecto criado no stack 2. É criada uma nova cópia no stack (passagem por cópia) 3. O objecto é copiado para o seu destino final 4 4. A cópia do stack local do stack é destruída Se os objectos forem grandes, isto é terrivelmente ineficiente!

65 65 A solução Passar os objectos sempre por const&, caso os mesmos não sejam modificados e seja necessário algo que valha pelo objecto inicial!

66 66 » C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, it blows away your whole leg. « Bjarne Stroustrup » If you think C++ is not overly complicated, just what is a protected abstract virtual base pure virtual private destructor and when was the last time you needed one? « Tom Cargill Próxima Sessão: STL = Standard Template Library

67 67 Para saber mais... C++ How to Program, 4th Edition by Harvey M. Deitel, Paul J. Deitel Prentice Hall, Aug. 2002 Uma introdução leve ao C++ C++ Primer, 4th Edition by Stanley B. Lippman et. al. Addison-Wesley Professional, Feb. 2005 Para aprender ao pormenor tudo o que há a saber sobre C++

68 68 YOU ARE FREE TO USE THIS MATERIAL FOR YOUR PERSONAL LERNING OR REFERENCE, DISTRIBUTE IT AMONG COLLEGUES OR EVEN USE IT FOR TEACHING CLASSES. YOU MAY EVEN MODIFY IT, INCLUDING MORE INFORMATION OR CORRECTING STANDING ERRORS. THIS RIGHT IS GIVEN TO YOU AS LONG AS YOU KEEP THIS NOTICE AND GIVE PROPER CREDIT TO THE AUTHOR. YOU CANNOT REMOVE THE REFERENCES TO THE AUTHOR OR TO THE INFORMATICS ENGINEERING DEPARTMENT OF THE UNIVERSITY OF COIMBRA. (c) 2005 – Paulo Marques, pmarques@dei.uc.pt


Carregar ppt "Programação Orientada aos Objectos Paulo Marques Departamento de Eng. Informática Universidade de Coimbra Out/2005 Core C++: Uma abordagem."

Apresentações semelhantes


Anúncios Google