Wykład 8: Metaklasy. Interfejsy.

Podobne dokumenty
Wykład 6: Dziedziczenie.

Typy klasowe (klasy) 1. Programowanie obiektowe. 2. Założenia paradygmatu obiektowego:

Programowanie obiektowe

Programowanie obiektowe

Programowanie obiektowe

Programowanie obiektowe

Delphi Laboratorium 3

Zapis programu z wykorzystaniem modułu (Podstawy Delphi 2.1, 2.2, 2.3 str11 )

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Wykład 9: Polimorfizm i klasy wirtualne

Programowanie obiektowe

Klasy abstrakcyjne i interfejsy

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Wykład 1: Przegląd języka

Lista, Stos, Kolejka, Tablica Asocjacyjna

Kurs WWW. Paweł Rajba.

Podstawy Programowania semestr drugi. Wykład czternasty

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object

Zadanie polega na stworzeniu bazy danych w pamięci zapewniającej efektywny dostęp do danych baza osób.

Java Język programowania

Programowanie obiektowe - 1.

Wykład 7: Lazarus GUI

Polimorfizm. dr Jarosław Skaruz

Programowanie obiektowe Wykład 6. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14

Kurs programowania. Wykład 13. Wojciech Macyna. 14 czerwiec 2017

Zaawansowane programowanie w C++ (PCP)

Wykład 8: klasy cz. 4

PODSTAWY PROGRAMOWANIA

Dziedziczenie. dr Jarosław Skaruz

PARADYGMATY PROGRAMOWANIA Wykład 4

Zaawansowane programowanie w C++ (PCP)

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Dawid Gierszewski Adam Hanasko

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

Laboratorium z przedmiotu Programowanie obiektowe - zestaw 04

Kurs programowania. Wykład 9. Wojciech Macyna. 28 kwiecień 2016

PHP 5 język obiektowy

Wykład 4: Klasy i Metody

Programowanie obiektowe w VB cz 2

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

Języki i techniki programowania Ćwiczenia 2

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

10. Programowanie obiektowe w PHP5

Interfejsy i klasy wewnętrzne

Java: interfejsy i klasy wewnętrzne

Marcin Luckner Politechnika Warszawska Wydział Matematyki i Nauk Informacyjnych

Dziedziczenie jednobazowe, poliformizm

Klasy generyczne. ZbiórLiczb. ZbiórCzegokolwiek. Zbiór

Języki i techniki programowania Ćwiczenia 3 Dziedziczenie

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Programowanie obiektowe

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami.

java.util.* :Kolekcje Tomasz Borzyszkowski

Klasy abstrakcyjne, interfejsy i polimorfizm

Podstawy programowania. Wykład PASCAL. Zmienne wskaźnikowe i dynamiczne. dr Artur Bartoszewski - Podstawy prograowania, sem.

1. Programowanie obiektowe. Wstęp.

Projektowanie obiektowe oprogramowania Wykład 4 wzorce projektowe cz.i. wzorce podstawowe i kreacyjne Wiktor Zychla 2015

Podstawy programowania. Wykład PASCAL. Wstęp do programowania obiektowego. dr Artur Bartoszewski - Podstawy programowania, sem.

1. Klasa typu sealed. Przykład 1. sealed class Standard{ class NowyStandard:Standard{ // błd!!!

Tworzenie własnych komponentów

Programowanie RAD Delphi

Dziedziczenie. Tomasz Borzyszkowski

Wykład 5 Okna MDI i SDI, dziedziczenie

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Dokumentacja do API Javy.

Programowanie obiektowe

Zaawansowane programowanie w języku C++ Klasy w C++

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

Programowanie obiektowe i zdarzeniowe

STL Standardt Template Library (wprowadzenie)

Klasy. dr Anna Łazińska, WMiI UŁ Podstawy języka Java 1 / 13

Kurs rozszerzony języka Python

TEMAT : KLASY DZIEDZICZENIE

DIAGRAMY SYNTAKTYCZNE JĘZYKA TURBO PASCAL 6.0

Szablony klas, zastosowanie szablonów w programach

Projektowanie obiektowe oprogramowania Wykład 4 wzorce projektowe cz.i. wzorce podstawowe i kreacyjne Wiktor Zychla 2017

1 Atrybuty i metody klasowe

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Na przykładzie języków Java, C# i odrobiny C++ Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Spis treści. Wprowadzenie 15

Programowanie obiektowe

Wywoływanie metod zdalnych

Programowanie modułowe

.NET Klasy, obiekty. ciąg dalszy

Programowanie obiektowe w języku

Projektowanie obiektowe. Roman Simiński Polimorfizm

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Materiały do zajęć VII

Wykład 9: Metody wirtualne i polimorfizm

Co to jest klasa? Z programistycznego punktu widzenia klasa stanowi typ danych, który odwzorowuje wspólne cechy jakiegoś obiektu.

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

Programowanie w Internecie. Java

Typy sparametryzowane

Referencje do zmiennych i obiektów

Opis implementacji: Implementacja przedstawia Grę w życie jako przykład prostej symulacji opartej na automatach.

Materiały do laboratorium MS ACCESS BASIC

Transkrypt:

Kurs języka Object/Delphi Pascal na bazie implementacji Free Pascal. autor Łukasz Stafiniak Email: lukstafi@gmail.com, lukstafi@ii.uni.wroc.pl Web: www.ii.uni.wroc.pl/~lukstafi Jeśli zauważysz błędy na slajdach, proszę daj znać! Wykład 8: Metaklasy. Interfejsy. Zliczanie dowiązań. for..in. 1

Zagadnienia Dwie myśli przewodnie: Elastyczny polimorfizm. Automatyczne zarządzanie pamięcią. Trzy zagadnienia: Referencje klas (znane też jako metaklasy): typ class of i (elementy RTTI) pole Class. Konstruktory wirtualne. Interfejsy. Zliczanie dowiązań vs. model z właścicielem. Zwalnianie obiektów przez kontenery FPG. Jedno uzupełnienie: Jeszcze raz o składni for..in. 2

Referencje klas Pamiętasz metody klas? class procedure/class function Konstruktory też są metodami klas. Metody klasy wywołujemy jakby klasa była obiektem. Okazuje się, że jest! Typem klasy TExample jest: type TExampleClass = class of TExample Jeśli klasa TExample jest sealed, to typ TExampleClass jest jednoelementowy (singleton). Posługując się żargonem design patterns, obiekt TExample możemy nazwać singletonem. Jeśli TX jest podklasą TY, to TXClass jest podtypem TYClass. 3

Klasy są więc w Delphi (/Free) Pascalu first-class mamy parametryzację algorytmów i struktur danych typami przy typach generycznych: statycznie, przy referencjach klas: dynamicznie. Przekazywanie klas jako argumentów funkcji czy wartości zmiennych ma dopiero sens, gdy klasy te mają wirtualne metody klas. Tylko wirtualne mogą być inne niż z deklaracji zmiennej. Zastosowania: Wybór implementacji przez użytkownika, pluginy, algorytmy dynamicznie sparametryzowane ze względu na typy danych, abstrakcyjne algorytmy. 4

program singleton; uses SysUtils, Classes; type TBase = class class var b: Integer; class function VGetP: Integer; virtual; class procedure VSetP(const I: Integer); virtual; class function GetP: Integer; class procedure SetP(const I: Integer); TBaseClass = class of TBase; TDeriv = class sealed (TBase) class var d: Integer; class function VGetP: Integer; override; class procedure VSetP(const I: Integer); override; TDerivClass = class of TDeriv; 5

var MyClass0, MyClass1, MyClass2: TBaseClass; MyClass3: TDerivClass; MyClass0 := TBase; MyClass1 := TDeriv; MyClass2 := TDeriv; MyClass3 := TDeriv; MyClass3.VSetP(8); MyClass0.VSetP(3); MyClass2.VSetP(7); MyClass1.SetP(5); WriteLn ( MyClass0.VGetP=, MyClass0.VGetP, ; MyClass1.GetP=, MyClass1.GetP, ; MyClass2.VGetP=,MyClass2.VGetP, ; MyClass3.VGetP=, MyClass3.VGetP); WriteLn ( TBase.b=, TBase.b, ; TDeriv.d=,TDeriv.d); end. 6

unit plugin_form;... procedure TForm1.DeleteCtrlClick(Sender: TObject); var i: Integer; i := ListCtrls.ItemIndex; ListCtrls.Items.Objects[i].Destroy; ListCtrls.Items.Delete(i); procedure TForm1.EditCtrlClick(Sender: TObject); CurrentCtrl := TEdit; 7

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var NewCtrl: TControl; CtrlName: String; NewCtrl := CurrentCtrl.Create(self); NewCtrl.Visible := False; NewCtrl.Parent := self; NewCtrl.Left := X; NewCtrl.Top := Y; Inc (Counter); CtrlName := CurrentCtrl.ClassName + IntToStr(Counter); Delete(CtrlName, 1, 1); NewCtrl.Name := CtrlName; NewCtrl.Visible := True; ListCtrls.AddItem(CtrlName, NewCtrl); 8

Interfejsy Problemy z hierarchią podtypowania bazującą tylko na dziedziczeniu klas: Dziedziczenie implementacji niszczy enkapsulację (patrz item 16 Effective Java slajd 16 java-lecture11.pdf). Jednokrotne dziedziczenie nie można zdefiniować nowej klasy by pełniła wiele wcześniej zdefiniowanych ról. Słaba separacja specyfikacji i implementacji: część prywatna klasy musi być w interfejsie modułu, implementacja klasy musi być dostępna do skompilowania jej użycia. Interfejs to zbiór sygnatur (nagłówków) metod definiujący nowy typ. Obiekty tego typu pochodzą z klas implementujących interfejs: muszą mieć publicznie wszystkie metody z interfejsu. 9

Zalety interfejsów w Delphi(/Free) Pascalu: Mniejsza zależność między modułami, Jedna klasa może implementować wiele interfejsów-zachowań, które w Delphi(/Free) Pascalu mogą nawet mieć metody o tych samych nazwach, a różnych implementacjach w jednej klasie! Klasa może agregować zachowania, delegując do osobno zdefiniowanych klas implementację wybranych interfejsów w Delphi(/Free) Pascalu bez żadnego nadmiarowego kodu. Można opublikować interfejs rozprowadzając kod jedynie w postaci skompilowanej. Interfejsy mogą tworzyć hierarchię z wielodziedziczeniem. 10

Wady interfejsów w Delphi(/Free) Pascalu: W języku domyślnie interfejsy związane są ze zliczaniem dowiązań chociaż to zupełnie różne sprawy (cierpią i interfejsy i zliczanie dowiązań), Niezależność kompilacji tylko gdy trzymamy interfejsy w osobnych modułach niż (implementujące je) klasy. Pamiętaj by nie rozdmuchać nadmiernie architektury programów. Interfejsy są mniej popularne niż w Javie, częściowo przez popularność referencji klas. 11

program multi_intf; uses SysUtils, Classes; {$interfaces CORBA} type IBase = interface function ShowBase: String; IA = interface (IBase) function Show: String; IB = interface (IBase) function Show: String; 12

TMulti = class (IA, IB) function Show: String; function ShowBase: String; function ShowA: String; function ShowB: String; function IA.Show = ShowA; function IB.ShowBase = ShowB; function TMulti.Show: String; Show := TMulti.Show ; function TMulti.ShowBase: String; ShowBase := TMulti.ShowBase ; function TMulti.ShowA: String; ShowA := TMulti.ShowA ; function TMulti.ShowB: String; ShowB := TMulti.ShowB ; 13

var Multi: TMulti; Base: IBase; A: IA; B: IB; Multi := TMulti.Create; //Base := Multi; A := Multi; B := Multi; WriteLn ( A.ShowBase=, A.ShowBase, ; B.ShowBase=, B.ShowBase); WriteLn ( A.Show=, A.Show, ; B.Show=, B.Show); Multi.Free; end. 14

program no_counting; uses SysUtils, Classes; type IBasic = interface function Show: String; TNoCount = class (IBasic) function Show: String; function QueryInterface(constref IID:TGUID; out Obj) :LongInt; CDecl; function _AddRef:LongInt; CDecl; function _Release:LongInt; CDecl; function TNoCount.Show: String; Show := TNoCount.Show ; 15

function TNoCount.QueryInterface (constref IID:TGUID; out Obj):LongInt; CDecl; if GetInterface(IID, Obj) then Result := 0 function TNoCount._AddRef:LongInt; CDecl; Result := -1 function TNoCount._Release:LongInt; CDecl; Result := -1 16

var Basic: IBasic; NoCount: TNoCount; NoCount := TNoCount.Create; Basic := NoCount; WriteLn ( Basic.Show=, Basic.Show, ; NoCount.Show=, NoCount.Show); Basic := nil; NoCount.Destroy; end. 17

Zliczanie dowiązań O ile nie używamy {$interfaces CORBA}, zmienne, pola i parametry typu interfejsowego będą objęte tzw. zliczaniem dowiązań. Jest to mechanizm zarządzania pamięcią przejmujący od programisty obowiązek niszczenia obiektów (do których odnoszą się zmienne i pola typu interfejsowego). Inny mechanizm zarządzania pamięcią w Delphi/Free Pascalu to model z właścicielem (ownership): właściciel obiektu jest odpowiedzialny za jego destrukcję. Jest realizowny m.in. przez klasy pochodne z TComponent. Ponieważ komponenty korzystają z interfejsów dla zwiększenia polimorfizmu, TComponent implementuje metody obsługi zliczania dowiązań tak by one nic nie robiły. Zupełnie jak w przykładzie no_counting powyżej. Ostrożnie przy mieszaniu zmiennych typu klasy i zmiennych typu interfejsowego do przechowywania tych samych obiektów gdy zniknie ostatnia referencja poprzez typ interfejsowy, obiekt zostanie zniszczony, i możemy zostać z wiszącą (dangling) referencją w zmiennej typu klasy. 18

program bad_counting; uses SysUtils, Classes; type IBasic = interface function Show: String; TBadCount = class (TInterfacedObject, IBasic) function Show: String; function TBadCount.Show: String; Show := TBadCount.Show ; 19

var Basic: IBasic; BadCount: TBadCount; BadCount := TBadCount.Create; Basic := BadCount; WriteLn ( Basic.Show=, Basic.Show, ; BadCount.Show=, BadCount.Show); Basic := nil; BadCount.Destroy; end. Rozpoczęcie zliczania dowiązań dla BadCount. Znika jedyna referencja do BadCount więc obiekt zniszczony. Ups wiszący wskaźnik, naruszenie pamięci. 20

program interfaces_ref_counting; uses Classes; var C: TComponent; IO: TInterfacedObject; I: IInterface; C := TComponent.Create(nil); C := TComponent.Create(nil); I := C; I := TComponent.Create(nil); IO := TInterfacedObject.Create; IO := TInterfacedObject.Create; I := IO; I := TInterfacedObject.Create; end. Wyciek pamięci: brak zliczania dowiązań. Wycieki pamięci: co prawda wywołane zliczanie, ale nic nie robi, bo (tu też) TComponent je ignoruje. Wyciek pamięci bo zmienna nie jest interfejsowa. Brak wycieku, bo tutaj startujemy ze zliczaniem dowiązań. Preferowany sposób: brak możliwości pozostania z wiszącą referencją. 21

program ref_lifespan; type TClass = class (TObject, IInterface)... var GO: TClass; GI: IInterface; procedure LocalProc(Arg: IInterface); var LO: TClass; LI: IInterface; WriteLn ( Entered LocalProc ); Write( LO := TClass.Create; ); LO := TClass.Create( Local A ); Write( LI := LO; ); LI := LO; //raise Exception.Create( test exit ); //Exit; //Halt; Write( LI := GO; ); LI := GO; Write( GI := TClass.Create; ); GI := TClass.Create( Local B ); WriteLn ( Leaving LocalProc ); WriteLn ( Entered main program ); Write( GO := TClass.Create; ); GO := TClass.Create( Global A ); Write( GI := GO; ); GI := GO; LocalProc(TClass.Create ( Global B )); WriteLn ( Leaving main program ); end. 22

Entered main program GO := TClass.Create; Creating Global A GI := GO; Reference Global A increase to 1 Creating Global B Reference Global B increase to 1 Entered LocalProc LO := TClass.Create; Creating Local A LI := LO; Reference Local A increase to 1 LI := GO; Reference Global A increase to 2 Reference Local A decrease to 0 Destroying Local A GI := TClass.Create; Creating Local B Reference Local B increase to 1 Reference Global A decrease to 1 Leaving LocalProc Reference Global A decrease to 0 Destroying Global A Reference Global B decrease to 0 Destroying Global B Leaving main program Reference Local B decrease to 0 Destroying Local B Heap dump by heaptrc unit 4 memory blocks allocated : 64/64 4 memory blocks freed : 64/64 0 unfreed memory blocks : 0 23

Po odkomentowaniu raise Exception.Create( test exit ); zwróć uwagę że wszystkie obiekty TClass są zwolnione: Entered main program GO := TClass.Create; Creating Global A GI := GO; Reference Global A increase to 1 Creating Global B Reference Global B increase to 1 Entered LocalProc LO := TClass.Create; Creating Local A LI := LO; Reference Local A increase to 1 Reference Local A decrease to 0 Destroying Local A Reference Global B decrease to 0 Destroying Global B An unhandled exception occurred at $080485BC : Exception : test exit $080485BC LOCALPROC,... Reference Global A decrease to 0 Destroying Global A Heap dump by heaptrc unit 14 memory blocks allocated : 1165/1184 11 memory blocks freed : 1065/1080 3 unfreed memory blocks : 100 24

Po odkomentowaniu Exit;: Entered main program GO := TClass.Create; Creating Global A GI := GO; Reference Global A increase to 1 Creating Global B Reference Global B increase to 1 Entered LocalProc LO := TClass.Create; Creating Local A LI := LO; Reference Local A increase to 1 Reference Local A decrease to 0 Destroying Local A Reference Global B decrease to 0 Destroying Global B Leaving main program Reference Global A decrease to 0 Destroying Global A Heap dump by heaptrc unit 9 memory blocks allocated : 1017/1024 9 memory blocks freed : 1017/1024 0 unfreed memory blocks : 0 25

Po zamianie Exit; na Halt;: Entered main program GO := TClass.Create; Creating Global A GI := GO; Reference Global A increase to 1 Creating Global B Reference Global B increase to 1 Entered LocalProc LO := TClass.Create; Creating Local A LI := LO; Reference Local A increase to 1 Reference Global A decrease to 0 Destroying Global A Heap dump by heaptrc unit 9 memory blocks allocated : 1017/1024 7 memory blocks freed : 985/992 2 unfreed memory blocks : 32 26

Cykle i słabe referencje Jak widzieliśmy powyżej, zliczanie dowiązań jest w miarę niezawodne: jeśli procedura ma parametr lub zmienną lokalną typu interfejsowego, kompilator umieści jej kod w klauzuli try..finally żeby zawsze przy jej opuszczaniu dekrementować referencje znajdujące się w jej ramce stosu. Dodatkowa klauzula try może trochę pogorszyć wydajność. Ale zliczanie dowiązań nie zwolni obiektów jeśli tworzą one cykl. Gdy musimy mieć dane z cyklicznymi odwołaniami, i zależy nam na zarządzaniu pamięcią przez interfejsy-zliczanie dowiązań, to któreś z referencji trzeba przeciąć, np. przez zrzutowanie na wskaźnik uniwersalny Pointer albo przez referencję po oryginalnej klasie obiektu: byle nie mieć referencji po interfejsie. 27

Zarządzanie pamięcią w FGL TFPGObjectList zwalnia obiekty, o ile parametr FreeObjects dla konstruktora jest true (więc nie jest to konieczny mechanizm). Zwalnia nie tylko przy niszczeniu listy, także przy usuwaniu elementów z listy przez Remove, przez Clear etc. TFPGObjectList jest w pełni odpowiedzialne za zarządzanie pamięcią elementów. Jako ucieczka, zawsze można wyjąć element z listy przez Extract. TFPGInterfacedObjectList wydaje się zbędne: ma zakodowane wywoływanie _Add i _Release, ale ponieważ TFPGList posługuje się zwykłymi przypisywaniami, to kompilator automatycznie wrzuci te wywołania jeśli będziemy przechowywać listę interfejsów w liście. 28

Jeszcze raz o składni for..in Szczegóły i przykłady: patrz http://wiki.freepascal.org/for-in_loop. By korzystać ze składni for..in wystarczy by klasa kontenera implementowała metodę GetEnumerator, zwracającą obiekt-iterator mający metodę MoveNext przesuwającą iterator na następny element lub zwracającą False jeśli się nie da, oraz właściwość (tylko do odczytu) Current zwracającą aktualny element. Zawsze twórz obiekt-iterator jednorazowego użytku, bo będzie zwolniony po użyciu. Nie zwracaj kontenera jako iteratora! Iterować można po dowolnym obiekcie posiadającym GetEnumerator, więc możesz zaimplementować metody zwracające różne strategie enumeracji, najprościej jako iteratory posiadające dodatkowo GetEnumerator zwracającą self. Dla elegancji, kontener może implementować interfejs IEnumerable, i odpowiednio iterator interfejs IEnumerator. Możesz dorzucić składnię for..in do istniejącego typu przeciążając operator Enumerator. 29