Klasser i Delphi

Tags:    delphi
Skrevet af Bruger #29 @ 16.06.2001
Klasser i Delphi - Kort fortalt - Del 1.

Jeg vil igennem dette dokument, forklare hvordan man forbedrer sine programmer ved hjælp af klasser (classes) man selv laver. Jeg bruger de danske betegnelser for de forskellige engelske begreber, men jeg vil også skrive det engelske modstykke i parenteser, rundt omkring. Ordet egenskaber vil i mange tilfælde stå for både variabler (variables) og egenskaber (proberties). Metoder vil stå i stedet for procedure (procedures) og funktioner (functions).

Klasser er noget meget vigtigt at kende til, når man programmerer i et ObjektOrienteret Programmeringssprog (OOP). Uden at vide det, bruger du dem faktisk hele tiden - det er i hvert fald meget svært at undgå det. Det første eksempel på en klasse er TForm1. TForm1 er det vindue der som standard er der, når du åbner et nyt projekt. TForm1 har nogle egenskaber (proberties) og nogle metoder (methods). Tilsammen danner de objektet og din form TForm1.

Eks. 1
unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type

TForm1 = class(TForm)

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

end.
Ovenover er standardkoden i den form (TForm1) som Delphi 3 åbner op ved start eller når man vælger et nyt (almindeligt) projekt. Det kan være, at koden ser anderledes ud i den udgave, du bruger, men i det store hele, er det underordnet.

Gennemgang af klassers opbygning

Når man skriver klasser, skal de placeres efter type-ordet. Der må gerne være flere klasser i en unit, men mange gange er det smartere at placere klasser i units for sig selv, så kan du bare kalde dem i din uses-sektion.

I linen efter type står der TForm1 = class(TForm). Det er den sætning, der fortæller Delphi, at du vil oprette en klasse. I dette eksempel bliver der oprettet en klasse ved navn TForm1 med klassen TForm som stamfader. I stedet for class(TForm), kunne der bare have stået class, men så vil Delphi oversætte det til class(TObject), som er stamfaderen til alle objekter i Delphi. Derfor kan det alligevel ikke bruges her, men når du selv begynder at programmere klasser er det nyttig viden.

Stamfader mig her og stamfader mig der. Hvad går det nu ud på? Det er en af ideerne ved OOP - nemlig nedarving. Det er muligt at oprette en klasse og så videreudvikle den i andre klasser. I eks. 1. har folkene bag Delphi lavet en "simpel" form kaldet TForm, som så er stamfader til alle andre forme, som har deres helt egne særpræg. Her hedder den TForm1, men det kan jo ændres i Object Inspector'en (Name-egenskaben).

De næste linier indtil end; er indholdet i klassen. Der står hvad for nogle egenskaber og metoder, den pågældende klasse har. Der er fire mulige reserverede ord, men i dette eksempel er der kun brugt to: public og private. Det siger vidst sig selv, men jeg vil alligevel gennemgå dem.

Når man skriver en klasse, er der måske noget, som kun er nødvendigt for selve klassen og ikke dem der bruger den. Et eksempel kunne være en bil. I bilen er der nogle ting, som er nødvendige for at kunne køre. Det kunne være rat, pedaler eller sidespejl. Disse egenskaber er offentlig tilgængelige og skal stå efter public-ordet. Det er unødvendigt at vide størrelsen på rat, pedaler og sidespejl, men klassen skal bruge disse informationer, for at kunne lave en bil. Disse egenskaber skal stå efter private-ordet. Hvis man vil tilføje metoder, skal de tilføjes efter egenskaberne.

Det var en kort gennemgang af klassers opbygning, så nu burde du have fået lidt forståelse for dem.

Hvad skal man bruge dem til?

Godt spørgsmål! En klasse kan bruge til mange ting. En stor del af Delphi er bygget op omkring disse. Et par kendte eksempler på klasser er: En form, en knap, en editbox, TRegistry og mange andre. For lige at pensle det ud så er Delphi bygget med et objekt øverst (TObject), og så en masse "børn", som arver visse egenskaber fra førnævnte. På et tidspunkt kommer der en klasse, eller objekt, kaldet TComponent. Efter noget mere tid kommer vi til de komponenter, vi alle kender så godt. Man kunne også have gået en anden vej og have nået til f.eks. TRegistry eller en anden klasse.

Et eksempel på hvad man kan bruge dem til, er f.eks. den førnævnte bil. Hvis jeg skulle lave et spil, hvor der var en bil med, kunne jeg oprette en klasse med alt bilens funktionalitet i.

Eks. 2
TBil = class

private

RatStoerrelse, SideSpejlStoerrelse, PedalLaengde : integer;

GearModel : String;

{ Private declarations }

public

BilMaerke, BilNavn, BilType : String;

MaxFart : Integer;

{ Public declarations }

end;
Gennemgang af eksempel 2

Jeg vil lige starte med at sige (skrive) at det er et ret tyndt eksempel, men det burde være nok til at illustrere en klasse.

Øverst står der at vi vil oprette en klasse med navnet TBil, som har TObject som stamfader (det har alle objekter sjovt nok). Efter private-ordet erklærer vi fire variabler: Tre heltal (integers) og en tekststreng (string). Disse kan kun ses og bruges af selve den klasse der har oprettet dem. De tre tekststrenge og det ene heltal, kan bruges af alle. Både klassen, unitten og andre units, som kunne have brug for den pågældende klasse. Man kan så bygge videre på denne klasse, ved at oprette f.eks. TLastBil eller TFolkevogn. De arver så alle egenskaber og metoder klassen har.

Protected

Hvad nu hvis du vil lave en TLastBil og derfor skulle ratstørrelsen m.m. ændres. Det er ikke muligt, da de er erklæret som private egenskaber, men der er en løsning. Hvis man bruger ordet protected i stedet for, så kan alle klasser der nedstammer fra TBil pludselig få adgang til disse egenskaber. Det er meget rart at kunne lave denne "halve" privatisering (ok, her har ordet en lidt anden mening).

Metoder

I alle de eksempler jeg har brugt, er der ikke kommet nogle metoder med. Det er fordi jeg synes at vi lige skulle få det mest basale på plads. At tilføje en metode er rimelig simpelt. Nedenunder er der et simpelt eksempel, som du selv kan få frem ved at dobbeltklikke på en "ren" form.

Eks. 3
unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type

TForm1 = class(TForm)

procedure FormCreate(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);

begin

end;

end.

Gennemgang af eksempel 3

Kig lige eksemplet igennem og læg mærke til linien procedure FormCreate(Sender: TObject); og linierne:
procedure TForm1.FormCreate(Sender: TObject);
begin

end;
Den første linie fortæller Delphi at der er en procedure kaldet FormCreate(Sender: TObject); Du vil måske lægge mærke til at det ikke står efter et af de tre kendte reserverede direktiver, om du har lært indtil videre. Der er nemlig et fjerde direktiv kaldet published, men det vedrører kun komponentprogrammering, som vi ikke er nået til endnu. Her må jeg melde klart ud og sige at det her er rent gæt, men jeg mener altså, at Delphi opfatter den førnævnte procedure, som hvis den var skrevet efter published-ordet.

Den næste del du skulle lægge mærke til, ligger slet ikke inde i klassen…eller det gør den jo, for det er jo en metode, som er indeni en klasse. Hver gang du arbejder med forme, arbejder du også med klasser. Der er faktisk mange ting som er klasser, så du skal være meget god for at undgå dem. Læg mærke til at der står TForm1 i den sidste del, men det gør der ikke inde i klassen. Det er for at fortælle Delphi at det er en metode det ikke bare er en tilfældig procedure, men en der hører til klassen TForm1.

Nedarving

Jeg har været lidt inde på det før, men jeg gentager det lige endnu en gang for, at få dem på bagerste række med.

Det er muligt at nedarve egenskaber fra en klasse til en anden hvis man erklærer det. Erklæringen kan f.eks. se sådan ud: TCola = class(TDrik). Det er nødvendigt at oprette klassen TDrik først. Hvis man "glemmer" at skrive noget efter class, så læser Delphi det som om man opretter en klasse efter TObject, som er stamfaderen til alle klasser. Det er rimelig smart, for så behøver man ikke gøre det selv.

Når man så har erklæret ens klasse kan man bruge de informationer denne indeholder og videreudvikle den. F.eks. vil det være en god ide at tilføje noget brus til TCola-klassen.

Constructors og Destructors

Nu begynder det virkelig sjove. Som du måske har lagt mærke til, skal man, når man vil benytte en klasse, kalde den med såkaldt constructor. Du kender måske ikke ordet, men det er forhåbentlig ikke helt ukendt, at man lige kalder proceduren Create; før man begynder at bruge klassen. Det er gængs at bruge navnet Create som constructor, men andre eksempler er Init, Execute og Start.

Når man opretter en klasse vil der som standard være en constructor kaldet Create, som klassen nedarver fra TObject. Den kan man i mange tilfælde bare lade være som den er, men når man virkelig bliver hardcoreprogrammør, så er det mange gange en god ide at omskrive constructoren.

Hvorfor skulle man dog omskrive standardconstructoren, kunne man spørge. En af grundene er at du måske gerne vil initialisere en anden klasse eller sætte nogle standardværdier. Der må gerne være flere constructorer, men en er mange gange nok.

Constructors & destructors in real life

En Constructor oprettes på stort set samme måde som en procedure. I stedet for det reserverede ord procedure, bruges der ordet constructor til at fortælle Delphi at man vil oprette en constructor til en klasse.

Eks. 4


… (Medtages ikke)

type

TTestKlasse = class

private

{ Private declarations }

TekstStreng : String;

Heltal : Integer;

public

{ Public declarations }

constructor Create; override;

destructor Destroy; override;

end;

… (Medtages ikke)

implementation

constructor TTestKlasse.Create; override;

begin

inherited Create; {Medtag koden fra stamfaderklassen (TObject er stadig den øverste på
ranglisten også selvom vi ikke skriver det i koden)} TekstStreng := '1 2 3. Testing! 1 2 3.'; {Her sættes der en værdi} Heltal := 2000; {Og her} end; destructor TTestKlasse.Destroy; override; begin ShowMessage(TekstStreng); {Lav de sidste ting inden klassen ødelægges.} TekstStreng := 'Slut'; {F.eks. kunne man gemme en fil eller kalde andre destructors
fra andre klasser man har brugt} Heltal := 0; inherited Destroy; {Medtag koden fra stamfaderklassen (TObject er stadig den øverste på ranglisten
også selvom vi ikke skriver det i koden)} end;
Gennemgang af eksempel 4

I dette eksempel er det rimelig nemt at gennemskue hvad der sker. I klassen øverst oppe er der to linier, der er interessante:

constructor Create; override;
destructor Destroy; override;

Det er to næsten almindelige procedure, hvis man ser bort fra det første ord i hver linie (Det sidste ord i hver linie vil jeg ikke gennemgå i dette dokument, men der er hjælp at hente i hjælpefiler og bøger).

Det næste der er interessant er den anden linie i TTestKlasse.Create og den næstsidste i TTestKlasse.Destroy. Der bruges det reserverede ord inherited og, som mange nok har gættet, er det det samme som at gentage den kode, der er i stamfaderklassen. Læg mærke til at man skriver linierne forskellige steder alt efter om det er constructors eller destructors. I constructors er det før koden og i destructors er det efter (prøv selv at gætte hvorfor).

Notits!
I Delphi's hjælpefil påpeger de, at det ikke er smart at kalde en klasses destructor direkte, men i stedet bruge proceduren Free. I TObject ser den således ud:
procedure TObject.Free;

begin

if Self <> nil then Destroy;

end;


Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (1)

User
Bruger #2416 @ 20.06.03 19:53
Hejsa!

Jeg synes det er en rigtig god artikel, har lært meget af den! Jeg er dog ked af at der ikke er nogle eksempler på hvordan man bruger klassen. Sloes f.eks. med at skrive test.Create, istedet for test := TKlasse.Create;

Hilsen Anders
Du skal være logget ind for at skrive en kommentar.
t