Upload
internet
View
116
Download
2
Embed Size (px)
Citation preview
ProgramaçãoOrientada aos Objectos
Paulo MarquesDepartamento de Eng. InformáticaUniversidade de [email protected] O
ut/2
005
Core C++: Uma abordagem tutorial
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
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
Controlo de Fluxo (1)
5
Controlo de Fluxo (2)
6
Aspecto de um programa completo
// Importa biblioteca e passa a usar o espaço de nomes “standard”#include <iostream>#include <vector>using namespace std;
// Programa principalint main(){ // Declara uma tabela de inteiros de tamanho variável vector<int> 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 estruturasde dados e algoritmos!
Envio para o ecrã
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
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
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
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
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
A sua implementação
Inicialmente tem tamanho para um elemento(os elementos são guardados no heap!)
No final garante que todos os elementossão apagados!
13
A sua implementação (2)
O conjunto cresce automaticamente sempre que não existe mais espaço!
14
A sua implementação (3)
15
Pequeno programa de teste
16
Executando...
17
Qual é a grande limitação desta classe?
(Bem, uma das “grandes” limitações...)
Apenas permite lidar com inteiros. Mas, porquenão armazenar qualquer tipo de objectos??
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<int> c;c.adiciona(10);c.adiciona(20);
Conjunto<Pessoa> clientes;
Pessoa p(“João Carlos”, 20);clientes.adiciona(p);
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
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 garantirque o elemento ainda não existe.
21
MonoConjunto::adiciona()
Mas, qual é o problema com o seguinte código?
Nota: Conjunto<T>::adiciona(valor) chama explicitamenteo método adiciona da classe Conjunto. I.e. da classe acima!
22
MonoConjunto::adiciona()
Mas, qual é o problema com o seguinte código?
Elementos privados de Conjunto!
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
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
virtual?
Garante que case se usem ponteiros para aceder aos membrosdesta classe (ou derivadas) ou caso os mesmos sejam passados como parâmetros, são chamados os métodos correctos!
Porquê?
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
Porquê o destrutor virtual?
Qual o resultado da execução??
28
Porquê o destrutor virtual? (2)
O destrutor de Empregado nunca é chamado.MEMORY LEAK!!!
O sistema não sabequal o verdadeiro tipo associado aoobjecto apontado por p
29
Com um destrutor virtual...
Garante que os destrutores são sempre chamados correctamente
OK!
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
Conjunto Revisitado
32
MonoConjunto Revisitado
Nota: uma vez declarados como virtual na classe base não deixamde ser virtual nas classes derivadas. No entanto, é mais elegantetorná-lo explícito.
33
Implementação de MonoConjunto
Não é necessárianenhuma inicializaçãonem limpeza explícita
Afinal não é precisoaceder explicitamenteàs estruturas de dados(Óptimo!)
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
24
3
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
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
Conjunto Revisitado
38
Operadores como métodos
conversão
conversão
39
Implementação
40
Execução
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 !=, <, <=, >, >=)
+ - * / % ^ & | ~
! , = < > <= >= ++ --
<< >> == != && || += -= /=
%= ^= &= |= *= <<= >>= [] ()
-> ->* new new[] delete delete[]
42
ostream
Como enviar um conjunto para o ecrã?
Basta uma redefinição global...
Notas:
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 temacesso nem a _nome nem a _idade!
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
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
Qual o resultado da execução deste código?
47
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
10 20 30
conjA
3
4
_nElementos
_capacidade
_conjunto
conjB
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
10 20 30
conjA
3
4
_nElementos
_capacidade
_conjunto
conjB
Não se pode apagar a mesma memória duasvezes!
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<T>& 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
“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
Definição do Construtor de Cópia
53
Definição do operador de atribuição
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
O resultado da execução...
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
MonoConjunto revisitado
Classe base recomendadapara erros nas aplicações,devido ao programador!
Aqui acontece a excepçãoe o programa é abortado!
58
O resultado (espectável ou não...)
59
Salto para ohandler correspondente
60
Qual o aspecto do código?
61
Definir uma nova excepção (parametrizada )
62
As novas definições
63
Ainda resultando em...
As excepções são um tópico complexo e abrangente em C++. É importante consultar uma referênciaantes das começar a utilizar! Isto constitui apenasuma brevíssima introdução ao tópico!
64
Antes de terminarmos...
12
2
3
1. Objecto criado no stack
2. É criada uma nova cópiano stack (passagem porcópia)
3. O objecto é copiado parao seu destino final
4
4. A cópia do stack local dostack é destruída
Se os objectos forem grandes, isto é terrivelmente ineficiente!
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
» 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
Para saber mais...
C++ How to Program, 4th Editionby Harvey M. Deitel, Paul J. DeitelPrentice Hall, Aug. 2002
Uma introdução “leve” ao C++
C++ Primer, 4th Editionby Stanley B. Lippman et. al.Addison-Wesley Professional, Feb. 2005
Para aprender “ao pormenor” tudo o que há a saber sobre C++
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, [email protected]