Aula 6 Classes que reservam recursos externos 1 PilhaDeInt: interface /** Representa pilhas de int. @invariant itens aponta matriz com capacidade_actual itens e capacidade_inicial <= capacidade_actual e 0 <= número_de_itens <= capacidade_actual. */ class PilhaDeInt { public: typedef int Item; /** Constrói pilha vazia. @pre V. @post estáVazia(). */ PilhaDeInt(); /** Destrói a pilha. @pre V. @post recursos externos reservados foram libertados. */ ~PilhaDeInt(); 2 2003/2004 Programação Orientada para Objectos PilhaDeInt: interface /** Devolve o item que está no topo da pilha. @pre ¬estáVazia(). @post topo idêntico ao item no topo de *this. */ Item const& topo() const; /** Indica se a pilha está vazia. @pre V. @post estáVazia = *this está vazia. */ bool estáVazia() const; /** Devolve altura da pilha. @pre V. @post altura = altura de *this. */ int altura() const; 3 2003/2004 Programação Orientada para Objectos PilhaDeInt: interface /** Põe um novo item na pilha (no topo). @pre V. @post *this contém um item adicional no topo igual a novo_item. */ void põe(Item const& novo_item); /** Tira o item que está no topo da pilha. @pre ¬estáVazia(). @post *this contém os itens originais menos o do topo. */ void tiraItem(); 4 2003/2004 Programação Orientada para Objectos PilhaDeInt: implementação private: static int const capacidade_inicial = 32; int capacidade_actual; Item* itens; int número_de_itens; bool cumpreInvariante() const; }; inline PilhaDeInt::PilhaDeInt() : capacidade_actual(capacidade_inicial), itens(new Item[capacidade_actual]), número_de_itens(0) { assert(cumpreInvariante()); } 5 2003/2004 Programação Orientada para Objectos PilhaDeInt: implementação inline PilhaDeInt::~PilhaDeInt() { assert(cumpreInvariante()); delete[] itens; } PilhaDeInt::Item const& PilhaDeInt::topo() const { assert(cumpreInvariante()); return itens[número_de_itens - 1]; } 6 2003/2004 Programação Orientada para Objectos PilhaDeInt: implementação bool PilhaDeInt::estáVazia() const { assert(cumpreInvariante()); return altura() == 0; } int PilhaDeInt::altura() const { assert(cumpreInvariante()); return número_de_itens; } 7 2003/2004 Programação Orientada para Objectos PilhaDeInt: implementação void PilhaDeInt::põe(Item const& novo_item) { assert(cumpreInvariante()); if(número_de_itens == capacidade_actual) { Item* novos_itens = new Item[capacidade_actual * 2]; for(int i = 0; i != número_de_itens; ++i) novos_itens[i] = itens[i]; capacidade_actual *= 2; delete[] itens; itens = novos_itens; } itens[número_de_itens] = novo_item; ++número_de_itens; assert(cumpreInvariante()); } 8 2003/2004 Programação Orientada para Objectos PilhaDeInt: implementação void PilhaDeInt::tiraItem() { assert(cumpreInvariante()); assert(not estáVazia()); --número_de_itens; assert(cumpreInvariante()); } bool PilhaDeInt::cumpreInvariante() const { return capacidade_inicial <= capacidade_actual and 0 <= número_de_itens <= capacidade_actual; } 9 2003/2004 Programação Orientada para Objectos Classes que reservam recursos externos Princípios usados: 1. 2. 10 Todas as variáveis dinâmicas construídas devem ser destruídas A entidade encarregue de construir deve tipicamente responsabilizar-se pela destruição: política quem constrói, destrói Recursos externos: memória, ficheiros, … 2003/2004 Programação Orientada para Objectos Construtor por cópia O que sucede depois de: PilhaDeInt p1; PilhaDeInt p2 = p1; // Ou PilhaDeInt p2(p1); 11 2003/2004 Programação Orientada para Objectos Construtor por cópia p1: PilhaDeInt itens: int* capacidade_actual: int 32 :int[32] itens[31]: int … itens[2]: int número_de_itens: int 0 P2: PilhaDeInt itens: int* capacidade_actual: int 32 número_de_itens: int itens[1]: int 0 itens[0]: int As duas pilhas partilham orgãos internos. 12 2003/2004 Programação Orientada para Objectos Construtor por cópia 13 O C++ fornece um construtor por cópia implícito que se limita a construir os atributos de instância da classe, copiando-os um a um 2003/2004 Programação Orientada para Objectos Valor vs. Referência Semântica de valor: Semântica de referência: 14 Iguais, mas independentes Um novo nome para a mesma coisa (identidade) Igualdade ≠ Identidade 2003/2004 Programação Orientada para Objectos Construtor por cópia: declaração class PilhaDeInt { public: … /** Constrói pilha igual a original. @pre V. @post *this = original. */ PilhaDeInt(PilhaDeInt const& original); … Sempre por referência! private: … }; 15 2003/2004 Programação Orientada para Objectos Construtor por cópia: definição PilhaDeInt::PilhaDeInt(PilhaDeInt const& original) : capacidade_actual(?), itens(?), número_de_itens(?) { assert(original.cumpreInvariante()); ? assert(cumpreInvariante()); // assert(*this == original) se definirmos o operador ==. } 16 2003/2004 Programação Orientada para Objectos Construtor por cópia: definição PilhaDeInt::PilhaDeInt(PilhaDeInt const& original) : capacidade_actual(?), itens(?), número_de_itens(original.número_de_itens) { assert(original.cumpreInvariante()); ? assert(cumpreInvariante()); // assert(*this == original) se definirmos o operador ==. } 17 2003/2004 Programação Orientada para Objectos Construtor por cópia: definição PilhaDeInt::PilhaDeInt(PilhaDeInt const& original) : capacidade_actual(original.capacidade_actual), itens(new Item[capacidade_actual]), número_de_itens(original.número_de_itens) { assert(original.cumpreInvariante()); ? assert(cumpreInvariante()); // assert(*this == original) se definirmos o operador ==. } 18 2003/2004 Programação Orientada para Objectos Construtor por cópia: definição PilhaDeInt::PilhaDeInt(PilhaDeInt const& original) : capacidade_actual(original.capacidade_actual), itens(new Item[capacidade_actual]), número_de_itens(original.número_de_itens) { assert(original.cumpreInvariante()); for(int i = 0; i != número_de_itens; ++i) itens[i] = original.itens[i]; assert(cumpreInvariante()); // assert(*this == original) se definirmos o operador ==. } 19 2003/2004 Programação Orientada para Objectos Atribuição por cópia O que acontece depois de: PilhaDeInt p1; for(int i = 0; i != 3; ++i) p1.põe(i); PilhaDeInt p2; p2 = p1; 20 2003/2004 Programação Orientada para Objectos Atribuição por cópia Fuga de memória. :int[32] :int[32] itens[31]: int itens[31]: int … … 32 itens[2]: int itens[2]: int número_de_itens: int 2 3 itens[1]: int p1: PilhaDeInt itens: int* capacidade_actual: int p2 PilhaDeInt itens: int* capacidade_actual: int 32 número_de_itens: int itens[1]: int 3 0 1 itens[0]: int itens[0]: int 0 As duas pilhas partilham orgãos internos. 21 2003/2004 Programação Orientada para Objectos Atribuição por cópia Inicialização ≠ Atribuição O C++ fornece implicitamente às classes um operador de atribuição por cópia que atribui cada uma das variáveis membro de instância 22 Já existe um objecto, que tem de mudar de valor se existirem constantes ou referências de instância este operador não é fornecido 2003/2004 Programação Orientada para Objectos Atribuição por cópia: declaração class PilhaDeInt { public: … /** Torna *this igual a modelo. @pre V. @post *this = modelo. */ PilhaDeInt& operator = (PilhaDeInt const& modelo); … private: … }; 23 2003/2004 Programação Orientada para Objectos Atribuição por cópia: definição PilhaDeInt& PilhaDeInt::operator = (PilhaDeInt const& modelo) { assert(cumpreInvariante() and modelo.cumpreInvariante()); ? assert(cumpreInvariante()); // assert(*this == original) se definirmos o operador ==. } 24 2003/2004 Programação Orientada para Objectos Atribuição por cópia: definição PilhaDeInt& PilhaDeInt::operator = (PilhaDeInt const& modelo) { assert(cumpreInvariante() and modelo.cumpreInvariante()); ? capacidade_actual = modelo.capacidade_actual; número_de_itens = modelo.número_de_itens; assert(cumpreInvariante()); // assert(*this == original) se definirmos o operador ==. } 25 2003/2004 Programação Orientada para Objectos Atribuição por cópia: definição PilhaDeInt& PilhaDeInt::operator = (PilhaDeInt const& modelo) { assert(cumpreInvariante() and modelo.cumpreInvariante()); delete[] itens; itens = new Item[modelo.capacidade_actual]; ? capacidade_actual = modelo.capacidade_actual; número_de_itens = modelo.número_de_itens; assert(cumpreInvariante()); // assert(*this == original) se definirmos o operador ==. } 26 2003/2004 Programação Orientada para Objectos Atribuição por cópia: definição PilhaDeInt& PilhaDeInt::operator = (PilhaDeInt const& modelo) { assert(cumpreInvariante() and modelo.cumpreInvariante()); delete[] itens; itens = new Item[modelo.capacidade_actual]; for(int i = 0; i != modelo.número_de_itens; ++i) itens[i] = modelo.itens[i]; capacidade_actual = modelo.capacidade_actual; número_de_itens = modelo.número_de_itens; assert(cumpreInvariante()); // assert(*this == original) se definirmos o operador ==. } 27 2003/2004 Programação Orientada para Objectos Atribuição por cópia O que deveria acontecer depois de: PilhaDeInt p; p.põe(1); p.põe(2); p = p; 28 Nada! 2003/2004 Programação Orientada para Objectos Mas o que acontece é… p1: PilhaDeInt itens: int* capacidade_actual: int :int[32] :int[32] itens[31]: int itens[31]: int … … itens[1]: int itens[1]: int 2 ? itens[0]: int itens[0]: int 1 ? 32 número_de_itens: int 2 29 2003/2004 Lixo! Programação Orientada para Objectos Atribuição por cópia: definição PilhaDeInt& PilhaDeInt::operator = (PilhaDeInt const& modelo) { assert(cumpreInvariante() and modelo.cumpreInvariante()); if(*this != modelo) { Não está definido! delete[] itens; itens = new Item[modelo.capacidade_actual]; Mas, como comparar pilhas? for(int i = 0; i != modelo.número_de_itens; ++i) itens[i] = modelo.itens[i]; capacidade_actual = modelo.capacidade_actual; número_de_itens = modelo.número_de_itens; } assert(cumpreInvariante()); // assert(*this == modelo) se definirmos o operador ==. } 30 2003/2004 Programação Orientada para Objectos Igualdade vs. Identidade Identidade - Alteridade Igualdade - Desigualdade 31 Se duas coisas são a mesma, então são iguais Se duas coisas são diferentes, então são outras Se duas coisas são iguais podem ou não ser a mesma O endereço é que marca a identidade das instâncias 2003/2004 Programação Orientada para Objectos Igualdade vs. Identidade Sejam i e j dois nomes de int int i = 0; int& j = i; ou Como saber se i e j são a mesma variável? int i = 0; int j = i; 32 2003/2004 Programação Orientada para Objectos Igualdade vs. Identidade Duas instâncias são a mesma se e só se tiverem o mesmo endereço! &i == &j é o mesmo que dizer que i e j são o mesmo indivíduo ou instância i == j não implica &i == &j &i == &j implica i == j 33 2003/2004 Programação Orientada para Objectos Atribuição por cópia: definição PilhaDeInt& PilhaDeInt::operator = (PilhaDeInt const& modelo) { assert(cumpreInvariante() and modelo.cumpreInvariante()); if(&*this != &modelo) { delete[] itens; itens = new Item[modelo.capacidade_actual]; for(int i = 0; i != modelo.número_de_itens; ++i) itens[i] = modelo.itens[i]; & e * são o inverso um do outro. capacidade_actual = modelo.capacidade_actual; número_de_itens = modelo.número_de_itens; } assert(cumpreInvariante()); // assert(*this == modelo) se definirmos o operador ==. } 34 2003/2004 Programação Orientada para Objectos Atribuição por cópia: definição PilhaDeInt& PilhaDeInt::operator = (PilhaDeInt const& modelo) { assert(cumpreInvariante() and modelo.cumpreInvariante()); if(this != &modelo) { delete[] itens; itens = new Item[modelo.capacidade_actual]; for(int i = 0; i != modelo.número_de_itens; ++i) itens[i] = modelo.itens[i]; capacidade_actual = modelo.capacidade_actual; número_de_itens = modelo.número_de_itens; } assert(cumpreInvariante()); // assert(*this == modelo) se definirmos o operador ==. } 35 2003/2004 Programação Orientada para Objectos Atribuição por cópia: definição PilhaDeInt& PilhaDeInt::operator = (PilhaDeInt const& modelo) { assert(cumpreInvariante() and modelo.cumpreInvariante()); if(this != &modelo) { if(capacidade_actual != modelo.capacidade_actual) { delete[] itens; itens = new Item[modelo.capacidade_actual]; } for(int i = 0; i != modelo.número_de_itens; ++i) itens[i] = modelo.itens[i]; Reciclagem da matriz. capacidade_actual = modelo.capacidade_actual; número_de_itens = modelo.número_de_itens; } assert(cumpreInvariante()); // assert(*this == modelo) se definirmos o operador ==. } 36 2003/2004 Programação Orientada para Objectos Classes que reservam recursos externos Construção (Construtor por cópia) Destruição (Destrutor) Cópia (Operador de atribuição por cópia) Igualdade ≠ Identidade Semântica de valor vs. semântica de referência 37 2003/2004 Programação Orientada para Objectos Aula 6: Sumário Classes que reservam recursos externos 38 Problemas comuns Construtores e destrutores Construção por cópia Semântica de valor vs. semântica de referência Atribuição por cópia O exemplo das pilhas 2003/2004 Programação Orientada para Objectos