1 / 17

Uma função de destruição pode ser uma função virtual .

Uma função de destruição pode ser uma função virtual. 3.4. Destrutor virtual ( virtual destrutor ). Vamos considerar o seguinte programa:. class B { int* a; public: B() { a = new int[2]; cout &lt;&lt; &quot;constr_B<br>&quot;; } ~B(){ delete[] a; cout &lt;&lt; &quot;destr_B<br>&quot;; } };.

vaughan
Download Presentation

Uma função de destruição pode ser uma função virtual .

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Uma função de destruição pode ser uma função virtual. 3.4. Destrutor virtual (virtual destrutor) Vamos considerar o seguinte programa: class B { int* a; public: B() { a = new int[2]; cout << "constr_B\n"; } ~B(){ delete[] a; cout << "destr_B\n"; } }; class D : public B { int* b; public: D() { b = new int[4]; cout << "constr_D\n"; } ~D(){ delete[] b; cout << "destr_D\n"; } }; Os resultados: int main(int argc, char* argv[]) { B *pb = new D; delete pb; return 0; } constr_B constr_D destr_B

  2. memória Neste exemplo temos o problema com as chamadas dos destrutores. a = new int[2]; constr_B constr_D destr_B b = new int[4]; delete[] a; A ordem pela qual os construtores são chamados é a seguinte: os primeiros construtores a serem chamados são os da classe base e só depois os construtores da classe derivada. Os destrutores devem ser chamados exactamente pela ordem inversa.

  3. Este problema é completamente resolvido se o destrutor fosse virtual. class B { int* a; public: B() { a = new int[2]; cout << "constr_B\n"; } virtual ~B(){ delete[] a; cout << "destr_B\n"; } }; class D : public B { int* b; public: D() { b = new int[4]; cout << "constr_D\n"; } virtual ~D(){ delete[] b; cout << "destr_D\n"; } }; Os resultados: constr_B constr_D destr_D destr_B

  4. memória a = new int[2]; constr_B constr_D destr_D destr_B b = new int[4]; delete[] b; delete[] a; Os construtores são chamados pela seguinte ordem: os primeiros construtores a serem chamados são os da classe base e só depois os construtores da classe derivada. Os destrutores são chamados exactamente pela ordem inversa.

  5. Como se pode ver, a destruição foi feita adequadamente. Isto acontece porque o objecto tem o que se chama tabela de funções virtuais. Esta tabela é criada para todos os objectos definidos dentro de uma classe base, e contêm ponteiros para as funções que usam o objecto. Se o objecto pertencer a uma classe derivada, este contêm uma tabela adicional para as funções usadas na classe derivada. O que é preciso reter é que o mecanismo que chama automaticamente o destrutor baseia-se em endereços fornecidos por aquela tabela e quando o destrutor é virtual, esse mecanismo automático procura a tabela de funções virtuais que está no fim (ou seja, a tabela correspondente à classe derivada), executa esse destrutor passando de seguida para o destrutor indicado na tabela de funções virtuais que está directamente acima. Geralmente, é bom princípio fornecer um destrutor virtual em todas as classes que funcionam como classes base dado que os objectos da classe derivada são tipicamente manipulados (e possivelmente apagados) através de um ponteiro para a base.

  6. Um construtor não pode ser virtual porque necessita informação acerca do tipo exacto do objecto a ser criado para o poder construir correctamente. Para além disso, um construtor não é uma função normal. Esta pode interagir com as rotinas de gestão de memória de uma forma que funções membro normais não podem, e é diferente das funções membro normais já que não pode ser invocada para um objecto já existente. Por consequência não podemos ter um ponteiro para um construtor. Podemos definir uma função que chama um construtor e retorna o objecto construído. Isto é útil porque pode haver necessidade de criar um objecto sem sabermos o tipo exacto deste. Vamos considerar um exemplo onde objectos de uma classe podem fornecer ao utilizador uma cópia de si próprios ou um objecto do seu tipo.

  7. class expressao { public: virtual expressao* expressao_nova() { return new expressao(); } expressao(const expressao&); expressao(); }; class condicao : public expressao { public: expressao* expressao_nova() { return new condicao(); } condicao(const condicao&); condicao(); }; void utilizador(expressao* p) { expressao* p2 = p->expressao_nova(); } Isto significa que dado um objecto da classe expressao, um utilizador pode criar um novo objecto “que é realmente do mesmo tipo”.

  8. Sumário de construtores 1. Construtores são as rotinas utilizadas em C++ para construir objectos. 2. Todas as classes têm um construtor. 3. Se numa classe não estiver declarado um construtor o compilador encarrega-se de implementar um. 4. Os construtores implementados pelo compilador são públicos. 5. O construtor é uma função com o mesmo nome da classe a que pertence. 6. O construtor pode receber parâmetros. 7. Um construtor da classe X não pode ter parâmetros da classe X, mas pode ter como parâmetro uma referência à classe X (X&). 8. Um construtor declarado como X::X(const X&) é o construtor de cópia. 9. O construtor pode ter valores para serem usados por defeito. 10. O construtor não pode devolver qualquer valor, nem mesmo void.

  9. 11.Para que se possa definir um array de objectos de uma classe com objectos membros declarados constantes, essa classe tem que ter, no construtor, parâmetros indicados para serem usados por defeito. 12. Não é possível obter um ponteiro para o construtor. 13. Um construtor por defeito é um construtor que pode ser chamado sem argumentos. 14. O construtor de uma classe é chamado automaticamente quando um objecto dessa classe é declarado. 15. É possível fazer a sobrecarga (overloading) do nome do construtor. 16. O construtor não pode ser herdado por classes derivadas; no entanto as classes derivadas podem chamar o construtor da classe base. 17. O construtor não pode ser declarado como virtual. 18. Se o construtor, por alguma razão, não termina o seu serviço, o objecto não é automaticamente destruído. 19. Um objecto com construtor não pode ser membro de uma união.

  10. Sumário de destrutores 1. Destrutores são as rotinas utilizadas em C++ para destruir objectos. 2. Todas as classes tem um destrutor. 3. Se numa classe não estiver declarado um destrutor o compilador encarrega-se de implementar um. 4. Os destrutores implementados pelo compilador são públicos. 5. O destrutor é uma função com o mesmo nome da classe a que pertence, precedido por “~”. 6. O destrutor não pode receber parâmetros (nem mesmo void). 7. O destrutor não pode devolver qualquer valor (nem mesmo void). 8. Não é possível obter um ponteiro para um destrutor. 9. O destrutor de uma classe é chamado automaticamente quando o programa sai de um bloco (scope) onde um objecto dessa classe foi declarado. 10. O destrutor pode ser chamado. 11. O destrutor pode ser declarado como virtual.

  11. 12. O destrutor não pode ser herdado por classes derivadas, no entanto as classes derivadas podem chamar os destrutores das classes base. 13. Um objecto com um destrutor declarado não pode pertencer a uma união. 14. Para destruir um objecto que esteja na zona de memória free store deve usar-se o operador delete.

  12. O que é importante: 1. Perceber o que são tipos da memória. 2. Perceber o que são construtores de cópia (copy construtors). 3. Perceber construção e destruição em classes derivadas. 4. Perceber o que é destrutor virtual. 5. Perceber as respectivas regras da linguagem C++. 6. Estar apto a construir programas que utilizem as respectivas construções de POO.

  13. class my_class { char* s; int b; int a; public: void display() {cout << a << '\t' << b << '\t' << s << endl;} my_class(int A, int B, char *S) : a(A), b(B), s(S) {} virtual ~my_class(); }; Agora vamos considerar alguns exemplos que vão demonstrar algumas construções diferentes da linguagem C++ void funR(my_class& M) { M.display(); } void fun(my_class M) { M.display(); } int main(int argc, char* argv[]) { my_class mc=my_class(19,95,"How are you?"); my_class MC(50,2000,"Aveiro"); mc.display(); MC.display(); fun( my_class(1,2,"Regards") ); funR( my_class(3,4,"Hello") ); return 0; } 19 95 How are you 50 2000 Aveiro 1 2 Regards 3 4 Hello

  14. class my_class { char* s; int b; int a; public: void display() {cout << a << '\t' << b << '\t' << s << endl;} void display_hello() {cout << "hello\n"; } my_class(int A, int B, char *S) : a(A), b(B), s(S) {} virtual ~my_class(); }; void f(my_class& M, void (my_class::*p)()=my_class::display) { M.display(); (M.*p)(); }; int main(int argc, char* argv[]) { //...................... f(mc); f(MC,my_class::display_hello); return 0; } 19 25 How are you 19 25 How are you 50 2000 Aveiro hello

  15. class B { //................ public: virtual ~B(){ cout << "destr_B\n"; } protected: B(int); }; int main(int argc, char* argv[]) { B my_b(3); // cannot access protected member declared in class 'B' // .................

  16. class D : public B { //..... public: D(int); ~D(){ cout << "destr_D\n"; } }; B::B(int for_b) { cout << "class B: " << for_b << endl; } D::D(int for_d) : B(for_d) { cout << "class D: " << for_d << endl; } Os resultados: int main(int argc, char* argv[]) { D my_D(3); return 0; } class B: 3 class D: 3 Destr_D Destr_B

  17. Os resultados: class B: 3 class D: 3 class DD: 3 Destr_DD Destr_D Destr_B class DD : public D { public: DD(int); ~DD(); }; B Construtores D Destrutores DD::DD(int for_dd) : D(for_dd) { cout << "class DD: " << for_dd << endl; } DD DD::~DD() { cout << "destr_DD\n"; } int main(int argc, char* argv[]) { DD my_DD(3); return 0; }

More Related