Objekter og referanser

Like dokumenter
OPPGAVESETT 7 OBJEKTER OG REFERANSER

public static <returtype> navn_til_prosedyre(<parameter liste>) { // implementasjon av prosedyren

public static <returtype> navn_til_prosedyre(<parameter liste>) { // implementasjon av prosedyren

OPPGAVE 1 OBLIGATORISKE OPPGAVER (OBLIG 1) (1) Uten å selv implementere og kjøre koden under, hva skriver koden ut til konsollen?

Del 4 Noen spesielle C-elementer

2 Om statiske variable/konstanter og statiske metoder.

Oversikt. INF1000 Uke 1 time 2. Repetisjon - Introduksjon. Repetisjon - Program

Dagens tema INF1070. Vektorer (array er) Tekster (string er) Adresser og pekere. Dynamisk allokering

Vektorer. Dagens tema. Deklarasjon. Bruk

INF1000: Forelesning 7

Dagens tema. C-programmering. Nøkkelen til å forstå C-programmering ligger i å forstå hvordan minnet brukes.

Del 1 En oversikt over C-programmering

Programmeringsspråket C

Kort om meg. INF1000 Uke 2. Oversikt. Repetisjon - Introduksjon

Repetisjon: Statiske språk uten rekursive metoder (C1 og C2) Dagens tema Kjøresystemer (Ghezzi&Jazayeri 2.6, 2.7)

Dagens tema Kjøresystemer (Ghezzi&Jazayeri 2.6, 2.7)

INF1000: Forelesning 7. Konstruktører Static

Oversikt. Introduksjon Kildekode Kompilering Hello world Hello world med argumenter. 1 C programmering. 2 Funksjoner. 3 Datatyper. 4 Pekere og arrays

Runtimesystemer Kap 7 - I

Programmeringsspråket C Del 3

2 Om statiske variable/konstanter og statiske metoder.

Programmeringsspråket C Del 3

IN 147 Program og maskinvare

INF våren 2017

Programmeringsspråket C Del 3

Programmeringsspråket C Del 3

Programmeringsspråket C Del 3. Hans Petter Taugbøl Kragset

IN våren 2018 Tirsdag 16. januar

IN våren 2019 Onsdag 16. januar

Kapittel 1 En oversikt over C-språket

Innhold uke 4. INF 1000 høsten 2011 Uke 4: 13. september. Deklarasjon av peker og opprettelse av arrayobjektet. Representasjon av array i Java

Programmeringsspråket C

IN 147 Program og maskinvare

Oversikt. INF1000 Uke 2. Repetisjon - Program. Repetisjon - Introduksjon

LC191D Videregående programmering Høgskolen i Sør-Trøndelag, Avdeling for informatikk og e-læring. Else Lervik, januar 2012.

Programmeringsspråket C

Dagens tema. C-programmering. Nøkkelen til å forstå C-programmering ligger i å forstå hvordan minnet brukes.

Dagens tema INF1070. Vektorer (array-er) Tekster (string-er) Adresser og pekere. Dynamisk allokering

Anatomien til en kompilator - I

Programmering i C++ Løsningsforslag Eksamen høsten 2005

Runtimesystemer Kap 7 - I

Forkurs INF1010. Dag 2. Andreas Færøvig Olsen Tuva Kristine Thoresen

Løsningsforslag for eksamensoppgave, våren 2004

Del 3. Pekere RR 2016

INF1000: noen avsluttende ord

Hva er verdien til variabelen j etter at følgende kode er utført? int i, j; i = 5; j = 10; while ( i < j ) { i = i + 2; j = j - 1; }

Runtime-omgivelser Kap 7 - I

Dagens tema C, adresser og pekere

Ark 3 av 26. printf("i adresse %08x ligger b med verdien %d.\n", &b, b); printf("i adresse %08x ligger a med verdien %d.

INF1000 EKSTRATILBUD. Stoff fra uke 1-5 (6) 3. oktober 2012 Siri Moe Jensen

Programmeringsspråket C Del 2

Introduksjon til objektorientert programmering

Forkurs INF1010. Dag 2. Andreas Færøvig Olsen Gard Inge Rosvold Institutt for Informatikk, 14.

Anatomien til en kompilator - I

Dagens tema. Hva er kompilering? Anta at vi lager dette lille programmet doble.rusc (kalt kildekoden): Hva er kompilering?

Av Stein Gjessing, Institutt for informatikk, Universitetet i Oslo

Dagens tema INF1070. Signaturer. Typekonvertering. Pekere og vektorer. struct-er. Definisjon av nye typenavn. Lister

Data. Dette refereres til som objektets tilstander. Funksjonalitet. Dette refereres til som objektets metoder.

Programmeringsspråket C Del 2

Programmeringsspråket C Del 2

Introduksjon til objektorientert. programmering. Hva skjedde ~1967? Lokale (og globale) helter. Grunnkurs i objektorientert.

Beskrivelse av programmeringsspråket Compila15 INF Kompilatorteknikk Våren 2015

INF1010 våren 2008 Uke 4, 22. januar Arv og subklasser

Signaturer. Dagens tema. En vanlig feil int-funksjon. Dette kan noen ganger gi rare feilmeldinger: INF1070 INF1070 INF1070 INF1070

Endret litt som ukeoppgave i INF1010 våren 2004

Eks 1: Binærtre Binærtretraversering Eks 2: Binærtre og stakk

Jentetreff INF1000 Debugging i Java

Generiske mekanismer i statisk typede programmeringsspråk

Velkommen til. INF våren 2016

Oppsummering. Kort gjennomgang av klasser etc ved å løse halvparten av eksamen Klasser. Datastrukturer. Interface Subklasser Klasseparametre

6108 Programmering i Java. Leksjon 5. Tabeller. Roy M. Istad 2015

Ark 1 av 18. programmeringsspråkenes. Velkommen til IN 211. verden. IN 211 Programmeringsspråk

Dagens tema. Adresser som parametre Dynamisk allokering Signaturer Definisjon av nye typenavn Typekonvertering Pekere og vektorer

Klasser, objekter, pekere og UML. INF gruppe 13

INF225 høsten 2003 Prosjekt del 4: kodegenerering

Array&ArrayList Lagring Liste Klasseparametre Arrayliste Testing Lenkelister

i=0 Repetisjon: arrayer Forelesning inf Java 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker 0*0 0*2 0*3 0*1 0*4

Forelesning inf Java 4

INF1000 (Uke 5) Mer om løkker, arrayer og metoder

INF1000 : Forelesning 4

Løsningsforslag ukeoppg. 6: 28. sep - 4. okt (INF Høst 2011)

INF 1000 høsten 2011 Uke september

Gjøre noe i hele treet = kalle på samme metode i alle objekten. Java datastruktur Klassestruktur

Forkurs INF1010. Dag 1. Andreas Færøvig Olsen Tuva Kristine Thoresen

i=0 i=1 Repetisjon: nesting av løkker INF1000 : Forelesning 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker j=0 j=1 j=2 j=3 j=4

Informasjon Eksamen i IN1000 høsten 2017

Mer om C programmering og cuncurrency

INF1000 undervisningen INF 1000 høsten 2011 Uke september

UNIVERSITETET I OSLO

3 emner i dag! INF1000 Uke 5. Objekter og pekere. null. Litt om objekter, pekere og null Filer og easyio Litt mer om tekster

Object interaction. Innhold. Abstraksjon Grunnleggende programmering i Java Monica Strand 3. september 2007.

OBJEKTER SOM EN PROGRAMMERINGS-TEKNIKK

Litt om Javas class-filer og byte-kode

UNIVERSITETET I OSLO

GUI («Graphical User Interface») del 2

Enkle generiske klasser i Java

INF1000 (Uke 15) Eksamen V 04

INF1000 (Uke 15) Eksamen V 04

Transkript:

Objekter og referanser Datasegmentet heap Vi har sett at verdier i Java blir kopiert ved prosedyrekall. I programmering, ønsker man imidlertid ikke å kopiere objekter ved prosedyrekall. Dette er fordi objekter kan ha en virkårlig størrelse (i antall bytes), fordi størrelsen til objektet er avhengig av hvor mange variabler objekter inneholder. Desto flere variabler, desto større blir objektet i minne. I situasjoner der prosedyrekall gjøres ofte (f.eks. kall i en løkke), og objekter er en del av parameterlisten, kan kopiering av objekter være en stor staff for ytelse. For å unngå slik kopiering av objekter, bruker Java et annet dynamisk datasegment enn stabelen. Datamaskinen (eller rettere sagt operativsystemet) tilbyr et dynamisk datasegment for lagring av data utenfor stabelen (som også er et dynamisk datasegment). Dette segmentet refereres til som heap. Generelt, kan programmerer plassere virkårlige variabler på heap segmentet. Det programmerer må gjøre, er å spørre operativsystemet om å få tilgang til et gitt antall bytes på minne (f.eks. 500 bytes). Hvis operativsystemet godtar dette, vil den allokere nøyaktig 500 bytes til programmet. Programmereren kan da bruke disse 500 bytene til hva som helst. Når programmerer er ferdig med å bruke de 500 bytene, må han/hun gi beskjed til operativsystemet at de 500 bytene kan frigjøres. Slik frigjøring kalles deallokering av minne. Slik håndtering av minne, der vi i løpet av utføringen til programmet, allokerer og deallokerer data på dette heap datasegmentet kalles dynamisk minnehåndtering. I Java, gjøres minnehåndteringen automatisk, mens i andre språk, som C og C++, gjøres dette manuelt. Eksempel i C (ikke pensum) Programmeringsspråket C tilbyr flere prosedyrer for minnehåndtering, blant annet malloc, for allokering, og free, for deallokering. Eksempel, der vi plasserer en int variabel på heapen: int main(void) { // alloker 4 bytes på heapen (lengden til int) // malloc returnerer en peker til den første byten 1: int* minne = malloc(4); // sett verdien til bytene på heapen til 5: 2: *minne = 5; // skriv verdien ut til konsollen: 3: printf("%i", *minne); // dealloker de 4 bytene. Vi har ikke tilgang til disse // etter dette. 4: free(minne); Etter at instruksjonen i linje 1 er utført, kan minne til programmet se slik at (heapen angitt i grå, stabelen i rød):

Adresse Verdi (i desimaltall) 5000 (*minne) Ikke spesifisert 5001 Ikke spesifisert 5002 Ikke spesifisert 5003 Ikke spesifisert 10000-10003 (minne) 5000 Legg merke til at adressene til stabel- og heap-variabler ikke ligger "nærme" hverandre i minne. Dette er fordi begge minnesegmentene er dynamiske og vi ønsker litt "rom" imellom dem. Ved linje 2, endres verdiene på heapen til tallet 5: Adresse Verdi (i desimaltall) 5000 (*minne) 5001 5 5002 5003 10000-10003 (minne) 5000 Ved linje 4, mister vi tilgangen til de 4 bytene på heapen. Minne for programmet ser nå slik ut: Adresse Verdi (i desimaltall) 10000-10003 (minne) 5000 Plassering av objekter I Java, plasseres alle objekter på heapen. Dette gjøres via new-operatoren. Regelen i Java er at utelukkende objekter kan plasseres på heapen. Andre typer variabler, som int og char (kjent som primitive datatyper i Java), kan ikke plasseres manuelt på heapen av programmerer disse variablene blir plassert på stabelen istedenfor. Eksempel: // her blir et objekt av typen MinKlasse opprettet på heapen MinKlasse obj = new MinKlasse(); I programmeringsspråket C++ kan programmereren selv velge om objektet skal plasseres på stabelen eller heapen. Fordelen med å plassere objektet på stabelen er at objektet blir automatisk slettet etter at prosedyren objektet har blir opprettet i er ferdigkjørt (husk: alle variabler på stack framen blir slettet når stack framen flyttes av stabelen). Eksempel i C++: // I C++ oppretter man objekter på heapen med new-operatoren, // slik som i Java. Som i C, får man en peker som peker på første // byte til objektet på heapen MinKlasse* obj = new MinKlasse(); // I C++ opprettes objektet på stabelen hvis new ikke // brukes i deklarasjonen: MinKlasse obj(); // I C++ kan man også plassere andre typer på heapen, som int: int* i = new int; Objekter som er plassert på heapen må manuelt fjernes, da de ikke fjernes automatisk når man er ferdig å bruke dem, slik som med variabler plassert på stabelen. Eksempel på dette i C++:

void p() { 1: string* str = new string("noe data på heapen"); 2: int a = 5; int main() { 3: p(); 4: int a = 5; Etter at prosedyren p har utført instruksjonene sine, men fortsatt ligger på stabelen, kan minne til programmet se slik ut: variabel (adresse) Verdi *str (adresse 100) "noe data på heapen" a 5 str 100 a 5 Etter at prosedyren p har blitt fjernet fra stabelen, holder programmet på følgende data: variabel (adresse) Verdi *str (adresse 100) "noe data på heapen" a 5 Vi ser at stack framen til p (i blå) har blitt (automatisk) fjernet, mens string-dataene fortsatt ligger på heapen. Dette er et problem fordi dette er data som vi ikke kan få tak i! Dataene starter på minneadresse 100. Pekeren til denne adressen har imidlertid blitt fjernet, så programmet kan derfor ikke få tak i disse dataene (programmet vet ikke hvor de ligger). Dette kalles minnelekasje og er noe som ikke skal skje! Programmer som lekker minne bruker unødvendige ressurser. Dette er spesielt viktig for programmer som kjører over lang tid (f.eks. på servere, der programmer kjører over flere år). En løsning på dette problemet er å påse at man alltid tar vare på pekerene til heap-segmentet, slik at man aldri mister tilgangen til dataene. Dette kan oppnås i eksempelet over ved å returnere verdien til pekeren str: string* p() { string* str = new string("noe data på heapen"); int a = 5; return str; int main() { string* str_peker = p(); // vi har nå fortsatt tilgang til string dataene på heapen int a = 5; Denne fremgangsmåten krever at vi hele tiden påser at vi ivaretar peker-verdien slik at vi vet hvilken adresse vi skal gå til for å få tar i de aktuelle dataene på heapen. Hva hvis vi imidlertid ikke lenger trenger heap-dataene? Da blir det unødvendig mye arbeid for programmerer å ivareta peker-verdien.

I denne situasjonen er det bedre å frigjøre heap-dataene. I C++, kan man bruke delete operatoren for dette: void p() { string* str = new string("noe data på heapen"); int a = 5; // la oss si at vi ikke trenger heap-dataene mer: delete str; // fungerer på samme måte som free i C int main() { p(); int a = 5; Som vi ser, tilbyr C++ programmerer ulike valg for hvordan objekter håndteres i minne. I Java, automatiseres dette og minnehåndtering gjøres automatisk. Som nevnt, så plasserer Java alle objekter på heapen og vi får den samme problemstillingen med minnelekasje som over. Java utfører følgende løsning for å automatisk håndtere minnelekasje: for hvert objekt på heapen, assosier en teller til objektet. Telleren angir hvor mange pekere som peker på objektet. Når telleren er 0, er det umulig å nå objektet på heapen, og heap-dataene kan dermed frigjøres. Java abstraherer pekere med referanser. Før vi tar et konkret eksempel, så må vi definere denne datatypen. Datatypen referanse og Javas bruk av referanser Referanser er pekere med noen restriksjoner, som sjekkes av kompilatoren, for å gjøre bruken av peker-variabeler sikrere. Verdien til en peker kan være tre typer "adresser": en gyldig adresse (pekeren peker på en verdi av den gitte typen), en ugyldig adresse (adressen lagrer data vi enten ikke har tilgang til eller holder på data av en annen type), eller adressen 0. Eksempler i C: int a = 5; // la oss si verdien til variabelen ligger på adresse 50 int* p = &a; // verdien til p er 50: dette er en gyldig adresse int* p2 = p + 15; // verdien til p2 er 65: dette er en ugyldig adresse int* p3 = NULL; // verdien til p3 er 0: dette er en null-adresse Pekeren p peker på en gyldig adresse fordi vi vet at int-verdien 5 ligger der. Pekeren p2 er en ugyldig adresse fordi vi ikke har noen int-verdier på adresse 65. Pekeren p3 er en såkalt null-peker, med verdien 0. I programmering er konvensjonen slik at hvis verdien til en peker er null (0), så betyr det at pekeren ikke peker på noen verdier. Referanser kan referere (peke) til følgende typer adresser: noe gyldig eller null. Referanser kan dermed ikke referere til noe ugyldig, slik pekere kan. Dette fører til noen restriksjoner for referanser: man kan ikke angi verdier til referanser direkte og man kan ikke gjøre aritmetiske operasjoner, som addisjon (pekeren p2 i eksempelet over). Eksempel i C: int a = 5; // la oss si verdien til variabelen ligger på adresse 50 int& r = a;// referanser angis med & etter typen. Verdien til r er 50 int* nullp = NULL; // verdien til nullp er 0 int& r2 = *nullp; // verdien til r2 er også 0

I C ønsker man at referanser alltid refererer til en gyldig verdi, selv om det teknisk sett er mulig å sette referansen til 0, som over. I Java, kan referanser enten referere til noe gyldig eller null. I motsetning til C, er det OK å referere til 0, så lenge programmerer håndterer null-referansen korrekt. Eksempel i Java: String str = new String(); // str er en referansevariabel String str2 = null; // str2 er en nullreferanse Referansen str refererer til et gyldig string-objekt på heapen. Verdien til str er start-adressen til objektet på heapen. Referansen str2 er en nullreferanse verdien til str2 er 0. Programmerer på være forsiktig med håndteringen av null-referanser. Man må ikke prøve å aksessere metoder for en null-referanse fordi referansen ikke refererer til noe! (Det finnes ingen metode fordi det finnes ingen objekt). Eksempel: String str2 = null; str2.isempty(); Denne koden vil krasje programmet fordi Java finner ikke metoden isempty for objektet str refererer til (som ikke finnes). Merk at tabeller er objekter og javas tabell-variabler er referanser på samme måte som andre referanser: int[] tab = new int[10]; // tab refererer til et tabell-objekt på heap int[] tab2 = null; // tab2 er en null-referanse Ut ifra forelesningen om tabeller, der vi håndterte tabell-variabler som pekere, får vi samme oppførsel for parameteroverføring med referanser som med pekere. Se eksempelet fra forrige forelesning (immutability diskusjonen) for et konkret eksempel. Eksempel Java garbage collection Som nevnt tidligere håndterer Java automatisk minnehåndtering og minnelekasje. Siden Java ikke tilbyr programmerer å manuelt slette objekter fra heapen, vil javaprogrammer "lekke" minne. Minne som er lekket (dvs. objekter som ikke kan nås) slettes av javas garbage collector (GC), som er et eget program som kjører samtidig med ditt javaprogram. Dette GC-programmet vil spontant iterere gjennom alle objektene på heapen, sjekke dens referanseteller, og slette objekter som ikke refereres til lenger. Eksempel 1: 1: String str1 = new String("heap objekt 1"); // objekt 1 blir opprettet 2: String str2 = str1; // objekt 1 har to referanser mot seg 3: str1 = new String("heap objekt 2"); // objekt 1 "mister" en referanse 4: str2 = null; // objekt 1 har ingen referanser mot seg (kan slettes) 5: str1 = null; // objekt 2 har ingen referanser mot seg (kan slettes) Ved linje 2, kopieres referanseverdien fra str1 over til str2. Objektet str2 refererer til øker nå sin referanseteller med 1. Ved linje 3, refererer str1 til det nye objektet på heapen og refererer ikke lengre til objekt 1, som får sin referanseteller minket med 1. Begge objektene på heapen har nå en referanse mot seg. Når verdiene til referansene, str1 og str2, blir satt til null, har vi ingen referansevariabler som refererer til de to string objektene på heapen. Disse kan nå slettes av GC. Eksempel 2 parameteroverføring:

1: public static void p(string str) { 2: System.out.println(str); 3: str = new String("Nytt heap objekt"); 4: public static void main(string args[]) { 5: String str = new String("Objekt 1"); 6: p(str); 7: str = new String("Nytt heap objekt"); Ved parameteroverføring av referansevariabler, blir referansetellere økt med 1, siden parametere overføres via kopiering. Etter linje 5, har objekt 1 en referanse mot seg (str-variabelen). Ved linje 2 (etter at p har blitt kalt på linje 6), har objekt 1 to referanser mot seg (str i main og str i p). Når str refererer til et nytt objekt i linje 3, vil objekt 1 ha en referanse mot seg (str i main). Merk at stringobjektet som opprettes i linje 3 i p kan slettes umiddelbart etter at p er ferdig utført fordi strvariabelen i p blir slettet fra p sitt stack frame. Objekt 1 kan slettes ved linje 7, etter at str-variabelen refererer til et nytt string objekt. Objekter, referanser, metoder, og konstruktører La oss se på hvordan håndteringen av objekter og metoder fungerer 'under the hood'. For et konkret eksempel, la oss si vi ønsker å opprette objekter av typen Date (se Date.java for definisjon av klassen). C-syntaks er ikke pensum. Opprettelse av objekter For å opprette et objekt, må Java utføre følgende operasjoner: kalkulere størrelsen til objektet, i antall bytes X (gjøres ved kompilering) allokere X antall bytes på heapen overføre variablene til objektet til heapen Vi kan tenke oss følgende C prosedyre som oppnår dette: Date* create_dateobj() { // alloker data på heapen. sizeof prosedyren finner størrelsen // til et Date objekt. Date* date_ptr = malloc(sizeof(date)); // C kan håndtere variabler til Date automatisk // variabler aksesseres slik: date_ptr->day // returner pekeren: return date_ptr; Her antar vi at vi har allerede definert en datastruktur Date i C, som kan gjøres slik:

struct Date { int year; enum Month month; int day; string name; ; Kall på konstruktør En konstruktør er en void prosedyre som utføres automatisk etter opprettelse. Korrekt prosedyre blir kalt avhengig av hvilken parametere som har blitt angitt: void do_date_constructor(date* date_ptr, string name) { date_ptr->day = 1; date_ptr->month = January; date_ptr->year = 2015; date_ptr->name = name; Med følgende Java instruksjon: Date Christmas = new Date("Christmas"); Kan vi tenke oss at denne blir konvertert til følgende C instruksjoner: // opprett objektet: Date* christmas = create_dateobj(); // kall på konstruktør do_date_constructor(christmas, "Christmas"); Vi ser at vi må sende med en peker til date objektet til konstruktøren, slik at den opererer på korrekt date objekt. Dette trengs også når vi kaller på metoder Opprettelse av metoder (forskjellen mellom metoder og prosedyrer) En metode er en prosedyre som utføres relativ til et objekt. Hvis du ikke har et objekt, kan du ikke utføre en metode. En prosedyre, på den andre siden, kan utføres uavhengig av objekter; du trenger ikke å opprette et objekt for å utføre en prosedyre. Metoder er teknisk sett prosedyrer, med en ekstra 'feature': metoden har alltid en referanse til sitt objekt. I Java, brukes nøkkelordet this til å henvise til denne referansen. Når Java konverterer en metode til en prosedyre (i C), vil denne referansen legges til parameterlisten. For eksempel, konverteres setyear-metoden (i Date.java) til følgende C prosedyre: void setyear(date* date_ptr, int year) { if(year >= 0) { date_ptr->year = year; else { // print error message here Tilsvarende Java metode, med bruk av this-referansen:

void setyear(int year) { if(year >= 0) { this.year = year; else { // error message Kall på Java metoden: christmas.setyear(2016); Tilsvarende C kall: setyear(christmas, 2016); Deallokering av objekter Når man er ferdig med å bruke objektet, burde det slettes fra heapen. C prosedyre for dette: void delete_date(date* date_ptr) { free(date); I Java, kalles denne prosedyren av GC, som beskrevet over. I C++, blir destruktøren til klassen kalt når objektet slettes, på samme måte som konstruktøren blir kalt når objektet opprettes. Jeg har vedlagt den korresponderende C++ klassen for Date for referanse (ikke pensum).