Föreläsning 4 Tillägg till C-syntaxen

Relevanta dokument
Programmering i C++ EDA623 Objektorienterad programutveckling. EDA623 (Föreläsning 5) HT / 33

Innehåll. Användardefinierade typer. Användardefinierade typer Kategorier. Konstruktorer. Konstruktorer Två sätt att skriva initiering av medlemmar

Del2 Klasser, medlemmar och arv Ämnesområden denna föreläsning:

Klasser. Kapitel 2. Kapitel 2 - Klasser, medlemmar och arv. Klasser. Klasser Medlemmar Arv

Programmering i C++ EDA623 Mer om klasser. EDA623 (Föreläsning 6) HT / 26

TDIU01 - Programmering i C++, grundkurs

Föreläsning 5-6 Innehåll. Exempel på program med objekt. Exempel: kvadratobjekt. Objekt. Skapa och använda objekt Skriva egna klasser

Föreläsning 5-6 Innehåll

Del3 Klassanvändning, operatorer och pekare Ämnesområden denna föreläsning:

Det finns många flaggor till g++,

Operatoröverlagring. endast operatorsymboler definierade i C++ kan överlagras = += -= *= /= %= ^= &= = <<= >>= < > <= >= ==!= && > ->*, [ ] ( )

Dynamisk bindning och polymorfism

Programsystem konstruktion med C++ (2D1387) Innehåll. övning 2 klasser och arv

Idag. Javas datatyper, arrayer, referenssemantik. Arv, polymorfi, typregler, typkonvertering. Tänker inte säga nåt om det som är likadant som i C.

Arv: Fordonsexempel. Arv. Arv: fordonsexempel (forts) Arv: Ett exempel. En klassdefinition class A extends B {... }

Programmering i C++ EDA623 Arv. EDA623 (Föreläsning 6) HT / 42

Poster ( structar ) Postdeklarationer

Innehåll. Pekare Exempel

Innehåll. Pekare Exempel

Introduktion. Klasser. TDP004 Objektorienterad Programmering Fö 2 Objektorientering grunder

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

Kompilering och exekvering. Föreläsning 1 Objektorienterad programmering DD1332. En kompilerbar och körbar java-kod. Kompilering och exekvering

C++ Objektorientering - Klasser. Eric Elfving

KLASSER. Inkapsling Abstrakt datatyp Public och private. Klassmedlemmar Datamedlemmar Exempel Funktionsmedlemmar

Innehåll. 1 Typdeklarationer och typomvandling 2 Resurshantering. 3 Objektorientering, kort repetition. 4 Klasser

TDIU01 Programmering i C++

Föreläsning 5-7 Operatoröverlagring

Java, klasser, objekt (Skansholm: Kapitel 2)

Funktionens deklaration

Innehåll. Typomvandlingar (casting) Implicita Typomvandlingar. Typomvandlingar (casting) Implicita Typomvandlingar

TDIU01 - Programmering i C++, grundkurs

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Felsökning. Datatyper. Referenstyper. Metoder / funktioner

Innehåll. Pekaren this Självreferens. Klasser Resurshantering, representation. Överlagring av operatorer. Överlagring av operatorer

allokeras på stacken dynamiskt new delete

Typkonvertering. Java versus C

Minneshantering. Minneshantering. Minneshantering. Undvik pekare

Innehåll. 1 Kort om dynamisk polymorfism. 2 Arv i C++ 3 Multipelt arv. 4 Något om statisk polymorfism. class Container {

Minnestilldelning (allokering) och frigörande (avallokering) av minne

Objektorientering - Arv och polymorfi. Eric Elfving Institutionen för datavetenskap

Introduktion till arv

*:85/ID200V C++ HT07. Föreläsning 8 Medlemspekare Undantagshantering Namnrymder

Programsystemkonstruktion med C++: Övning 2. Karl Palmskog september 2010

TDIU20 - Objektorienterad programmering i c++ - föreläsning 4

TDDC76 - Programmering och Datastrukturer

Innehåll. Resurshantering. Resource handles. Minnesallokering. Minnesallokering Exempel: allokering på stacken. 6. Resurshantering

Synlighet. Namespace Scope-operatorn Klasser Vänner

Föreläsning 2 Objektorienterad programmering DD1332. Typomvandling

C++ Objektorientering - Klasser. Eric Elfving Institutionen för datavetenskap

1 Funktioner och procedurell abstraktion

(Man brukar säga att) Java är... Denna föreläsning. Kompilering av Java. Historik: Java. enkelt. baserat på C/C++ Allmänt om Java

Innehåll. Introduktion till objektorientering. OOP (objektorienterad programmering) Objekt, instanser, klasser

TDDC76 - Programmering och Datastrukturer

F4. programmeringsteknik och Matlab

TDDC76 Programmering och datastrukturer

Kapitel 3. Synlighet. Kapitel 3 - Klassanvändning, operatorer och pekare. Synlighet

Övningar Dag 2 En första klass

Innehåll. Typomvandlingar (casting) Implicita Typomvandlingar. Typomvandlingar (type casts) Explicita, namngivna typomvandlingar (C++-11)

Föreläsning 5 (6) Metoder. Metoder Deklarera. Metoder. Parametrar Returvärden Överlagring Konstruktorer Statiska metoder tostring() metoden javadoc

Vem är vem på kursen. Objektorienterad programvaruutveckling GU (DIT011) Kursbok Cay Horstmann: Big Java 3rd edition.

Tommy Färnqvist, IDA, Linköpings universitet

Ett enkelt program i C++, hello.cpp. #include <iostream> int main() { std::cout << "Hello World\n"; return 0; } C++, Övning 1

Innehåll. Konstruktorer vid arv Regler för basklassens konstruktor. Konstruktorer vid arv. Konstruktorer vid arv. Konstruktorer vid arv

SP:PROG3 HT12 Tenta

grundläggande C++, funktioner m.m.

Funktionspekare, inledning: funktionsanropsmekanismen. Anrop via pekare

Bankkonto - övning. Övning 2 Skriv en metod, geträntan, som returnerar räntan.

Programsystemkonstruktion med C++

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

TDDC76 - Programmering och Datastrukturer

Skapa, kopiera och destruera klassobjekt

Datatyper och kontrollstrukturer. Skansholm: Kapitel 2) De åtta primitiva typerna. Typ Innehåll Defaultvärde Storlek

Tillämpad programmering

1 Klasser och objektorientering Vad är objektorientering?

JAVA Mer om klasser och objektorientering

Övning från förra gången: readword

Övriga byggstenar. Övriga byggstenar. Några tips under programutveckling. Beroenden Pekare till funktioner Typkonvertering

Att deklarera och att använda variabler. Föreläsning 10. Synlighetsregler (2) Synlighetsregler (1)

Objektorienterad Programmering (TDDC77)

Föreläsning 8 Programmeringsteknik och Matlab 2D1312/2D1305. Klass Object, instans av klass public/private Klassvariabler och klassmetoder

I Skapa Hej.java och skriv programmet. I Kompilera med javac Hej.java. I Rätta fel och repetera tills du lyckas kompilera ditt program

TDIU01 - Programmering i C++, grundkurs

Det objektorienterade synsättet. Objekt. Datorprogrammet kan uppfattas som en slags modell av den verklighet programmet skall samverka med.

Lektionsuppgifter. TDDI14 Objektorienterad programmering. Lektionsplanering Lektion Lektion Lektion

Kapitel 6 - Undantag

C++ - En introduktion

OOP Objekt-orienterad programmering

UML. Klassdiagr. Abstraktion. Relationer. Överskugg. Överlagr. Aktivitetsdiagram Typomv. Typomv. Klassdiagr. Abstraktion. Relationer.

Att använda pekare i. C-kod

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Checklista. Föreläsning 1-2 Innehåll. Programmering.

Tentamen OOP

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Nedan skapar vi klassen Person innehållande datamedlemmar för förnamn, efternamn, ålder, längd och vikt:

Föreläsning 3-4 Innehåll. Diskutera. Metod. Programexempel med metod

DD2387 Programsystemkonstruktion med C++ Tentamen 1 Torsdag 7 januari 2016, 14:00-18:00

1 Namnkontroll (NameControl)

Static vs Dynamic binding Polymorfism. Objekt-orienterad programmering och design (DIT953) Niklas Broberg, 2018

Föreläsning 3-4 Innehåll

public och private Obs: private inte skyddar mot access från andra objekt i samma klass.

Vad är en klass? Övning 2. Konstruktorer. ffl copy-konstruktorn tar en instans av klassen som. ffl default-konstruktorn tar inga argument och kan

Programsystemkonstruktion med C++: Övning 1. Karl Palmskog september 2010

Transkript:

*:85/ID200V C++ HT07 Föreläsning 4 Tillägg till C-syntaxen Några småsaker Resten-av-raden -kommentarer // (som i Java) Datatypen bool med värdena false och true, implicit kompatibel med int (d.v.s. int-värden kan fortfarande användas som boolska värden men det är bekvämare och tydligare med bool) Deklarationssatser, d.v.s. deklarationer tillåtna bland satser (som i Java) Deklaration av styrvariabel i for-satsens initieringsuttryck (som i Java) Deklaration av styrvariabel (?) i ett if- eller while-villkor: if (char *ptr = strchr(line, \n )) *ptr = \0 ; else while (getchar()!= \n ) ; Deklarationsvidden för en sådan variabel är hela if-satsen inkl. else-grenen. Tyvärr får en sådan deklaration inte ingå i ett mer komplext uttryck Bild 47 1

Referenser En referens i C++ är ett nytt namn på ett utrymme. Först syntax, sedan användnig Referens deklareras med modifieraren & efter typen: int i = 135; int& iref = i; iref är nu ett annat namn på samma utrymme som variabeln i har. Referensnamnet kan användas på precis samma sätt som det ursprungliga namnet: iref = 177; samma effekt som i = 177; iref++; samma effekt som i++; int *ipek = &iref; samma effekt som int *ipek = &i; Det finns inget sätt att ändra en referens att referera till annat utrymme, en referens är knutet till samma utrymme under hela sin livstid (precis som den ursprungliga variabeln). En referens måste alltså knutas till utrymme genom initiering vid sin definition (eller snarare vid sin initiering - om referensen definieras som medlem i en klass eller struct kan den inte initieras vid definitionen utan i konstruktorns initieringslista) Bild 48 Obs skillnaden mot adresstagningsoperatorn! Obs att denna användning av &-tecknet är helt ny och har ingenting med adresstagningsoperatorn & att göra! Adresstagningsoperatorn är en operator, den skrivs framför en variabel (e.d.) i ett uttryck för att få fram adressen till variabeln: int i = 57; int* ipek; ipek = &i; addresstagning i ett uttryck Typmodifieraren & skrivs efter typnamnet i en deklaration och innebär att man deklarerar ett nytt namn för ett existerande utrymme: int& iref = i; typmodifierare i en deklaration Bild 49 2

const-referenser En vanlig referens måste knutas till ett redan namngivet utrymme, högerledet i initieringen måste alltså vara ett lvalue (representera ett utrymme): int i = 150; char *pek = NULL; int arr[4]; int& refi = i; ok char *& pekref = pek; ok int& ar3ref = arr[3]; ok int& felref = 13; fel!!! Eftersom referenser används väldigt mycket för argumentöverföring till funktioner ansågs denna begränsning för restriktiv. Man har därför infört att const-referenser (som alltså refererar till utrymmen vilkas innehåll inte kan förändras) får initieras med värden: const int& ref = 13; ok Vid en sådan initiering skapas ett anonymt utrymme där värdet lagras och referensen blir namnet på detta utrymme. Bild 50 Referenser, huvudsaklig användning Formella argument till funktioner som skall kunna ändra sina aktuella parametrar void swap(int& x, int& y){ int z = x; x=y; y=z; Precis som Pascals VAR-parametrar void funk(){ int a = 5, b = 7; swap(a, b); Formella argument till funktioner, där argumenten är större än en int eller en pekare (för att undvika onödig kopiering av objekten, om argumenten inte skall förändras deklareras de const). Det är alltså det rekommenderade sättet att skicka argument i C++! bool check_name(const Person& p, const string& name){ return p.get_name() == name; Bild 51 3

Referenser, huvudsaklig användning, forts. Returtypen för funktioner (eller operatorer) som skall returnera ett lvalue : class Text{ int siz; char *cptr; char& operator[](int index){ return cptr[index]; ; lvalue - en tilldelningsbar storhet void funk(){ Text namn( Jozef ); namn[2] = s ; Bild 52 Dynamisk minnesallokering För dynamisk minnesallokering resp. avallokering används operatorerna new och delete, båda i två varianter - för enskilda objekt eller arrayer: Person *perspek=new Person( Jozef, 13); Person *folk=new Person[5]; (ovan anropas default-kontruktorn för vart och ett av de 5 objekten) char *str=new char[1024]; Motsvarande avallokeringar: delete perspek; delete [] folk; delete [] str; Vid new allokeras utrymme och konstruktorn anropas. Vid delete anropas destruktorn och utrymmet avallokeras. Det finns ingen motsvarighet till realloc (men bibliotekets vektor<> ger ju en dynamisk arrayliknande samling och bibliotekets string är dynamisk, så det finns egentligen inget behov). Bild 53 4

Dynamisk minnesallokering, forts. Om det saknas utrymme på heapen vid new generas undantaget bad_alloc som kan fångas upp : try{ char *pek = new char[1024]; catch(std::bad_alloc&){ std::cerr << Out of memory, canceled!\n ; Man kan även installera en egen funktion som då anropas istället: void out_of_memory(){ std::cerr << Out of memory, program exits!\n ; std::exit(13); int main(){ std::set_new_handler(out_of_memory); char *pek = new char[1024]; Den installerade funktionen anropas i en loop tills allokeringen lyckats. Funktion borde alltså antingen friställa utrymme så att allokeringen lyckas eller terminera programmet. Bild 54 Dynamisk minnesallokering, forts. malloc, calloc, realloc och free kan användas men man måste vara konsekvent : allokerat med malloc, calloc eller realloc - avallokera med free, allokerat med new -avallokeramed delete Obs att X-alloc-funktionerna inte anropar konstruktorer och att free inte anropar destruktorer! new och delete kan definieras om (överlagras), t.ex. för att allokera resp. återlämna utrymme någon annanstans än på heapen (exempelvis i en på förhand allokerad pool av utrymmen). Detta gör att man t.ex. kan skapa en automatisk garbage collector för sina program. Överlagringen av new och delete kan göras globalt eller för enskilda klasser. Det finns även varianter av new där man kan begära t.ex. placering av objekten på angivna adresser i minnet o.s.v. Intressant som allt detta är faller det utanför ramen för denna kurs. Bild 55 5

Funktionsöverlagring Man kan ju ha flera funktioner med samma namn men olika argumentlistor. Samtidigt behöver inte aktuella argumentvärden vid ett funktionsanrop vara av exakt samma typ som formella argument, bara det går att omvandla med implicit konvertering, t.ex. void funk(double d); void gunk(){ int i=13; funk(i); // Fungerar som funk((double)i); Om det nu hade funnits flera funktioner funk() med möjlighet till implicit konvertering, t.ex. void funk(char c); void funk(long l); osv så måste det finnas regler för vilken funktion som väljs ut för anrop. Dessa regler följer på nästa bild. De kompletteras dessutom med följande: returtypen beaktas inte vid överlagring funktioner från olika deklarationsområden (scope) utom namnrymder överlagrar inte varandra, men kan hämtas in genom using-deklarationer för medlemsfunktioner: const-medlem kan överlagra icke-const-medlem Bild 56 Function overloading resolution Identifiering av funktioner och funktionsmallar som kan komma ifråga. Av funktionsmallar väljs den mest specialiserade. Rangordning av kandidater: Exakt överensstämmelse eller triviala konverteringar (array till pekare, funktion till pekare, icke-const till const ) Befordringskonverteringar (bool till int, char till int, float till double osv.) Standardkonverteringar (int till double, double till int, Subklass* till Basklass*, TYP* till void* osv.) Användardefinierade konverteringar (genom konstruktorer och konverteringsfunktioner) Variabla argumentlistor, dvs. sist i argumentlistan Bäst-passande funktion: bäst för minst ett argument, ej sämre än någon annan för övriga. Om flera funktioner passar lika bra är det fel (tvetydighet, ambiguity), måste lösas genom explicit konvertering av argumenten vid anrop - d.v.s. av tillämpningsprogrammeraren ( entydiggörande, disambiguering?). Om ingen funktion passar är det givetvis också fel. Kontroll om anrop av den valda funktionen är korrekt (synlighet, returtyp) Bild 57 6

Defaultargument Om en funktion (eller medlemsfunktion, eller konstruktor) har argument för vilka det finns ett meningsfullt default-värde kan detta värde anges vid deklarationen av funktionen och behöver då inte anges vid anrop: void skriv_tecken(int antal, char tecken= * ){ for(int i=0; i<antal; i++) cout << tecken; cout << endl; int main(){ skriv_tecken(10,? ); // Skriver tio? skriv_tecken(10); // Skriver tio * Argumenten med defaultvärden måste stå sist i argumentlistan. Defaultvärdet får bara anges en gång, om en funktion deklareras på ett ställe och definieras på ett annat så anges det antingen i deklarationen eller i definitionen. Obs att det inte får finnas en överlagrad funktion som gör att anropet blir tvetydigt, om det t.ex. finns void skriv_tecken(int antal) {.. så vet inte kompilatorn vilken av funktionerna som anropas vid anropet skriv_tecken(30); En konstruktor med endast argument med defaultvärden blir defaultkonstruktorn. Bild 58 Infogade (inline) funktioner Med nyckelordet inline framför en funktionsdefinition eller deklaration anger man för kompilatorn att man vill att maskininstruktionerna för funktionen skall kopieras in i objektkoden på varje ställe där anrop sker, istället för att lagras på en plats och skötas med subrutinanrop. Detta görs för mycket små funktioner, oftast funktioner som bara anropar en annan funktion, t.ex. Klocka operator+(const Klocka& k, int min){ Klocka(k.get_tot_min() + min); inline Klocka operator+(int min, const Klocka& k){ return k + min; // Anrop av ovanstående operator+, // vänder bara på argumenten inline-funktioner utgör bl. a. ett alternativ till preprocessormacros, som inte bör användas inline är bara ett önskemål, kompilatorn avgör om funktionen går att översätta på detta sätt Medlemsfunktion som definieras inne i en klassdefinition är implicit deklarerade inline (men man kan inline-deklarera en medlemsfunktion definierad utanför klassen också). Bild 59 7

Infogade funktioner och one-definition rule För att kompilatorn skall kunna lägga in koden för en inline-funktion istället för anrop måste den ha tillgång till hela funktionsdefinitionen vid kompilering av de moduler som anropar funktionen. Om funktionen hör till en annan modul måste alltså definitionen finnas i headerfilen! I C skulle detta skulle leda till problem om två olika moduler som inkluderade samma headerfil med en funktionsdefinition kompilerades var för sig och länkades ihop - i C89 är det inte tillåtet. I C++ gäller följande (något förenklat): inline-funktionsdefinitioner, klassdefinitioner och mallar (och endast dessa) får inkluderas i källkoder för olika moduler som sedan länkas ihop om de är textuellt exakt lika och har samma betydelse. I praktiken innebär det helt enkelt att man kan placera dessa deklarationer och definitioner i headerfiler. Fortfarande måste man gardera sig mot upprepad inkludering av samma headerfil med #ifndef-direktiv Fortfarande gäller också att definitioner av vanliga funktioner måste göras i källkodsfiler som kompileras separat. Bild 60 C:s typecast double d = 3.14; int i = (int) d; Bör inte användas (?) Explicit typkonvertering C++ gammaldagskonverteringar - funktionsnotation: int i = int(d); Kräver ett typnamn: void func(void *vptr){ typedef char* Charpointer; char *cptr = Charpointer(vptr); Standard C++-konverteringar ( nya ): static_cast<måltyp>(uttryck) const_cast<måltyp>(uttryck) dynamic_cast<måltyp>(uttryck) reinterpreted_cast<måltyp>(uttryck) int i = static_cast<int>(d); Bild 61 8

Explicit typkonvertering - konstruktorer Antag följande: class Text{ int siz; char *cptr; Text(char *str){ ; void funk(const Text& t){ Säg att vi vill anropa funk() med strängen Jozef. Text:s konstruktor kan användas för att konstruera ett temporärt Text-objekt ur en char *: funk(text( Jozef )); Detta är ju helt i enlighet med funktionsnotation för typkonverteringar. Detta kallas för användardefinierad konvertering. Det finns även en annan variant av användardefinierad konvertering, tas upp senare. Bild 62 Implicit typkonvertering Den explicita konverteringen av char * till Text på föregående bild är inte nödvändig, det hade fungerat lika bra att anropa funk så här: funk( Jozef ); Detta beror på att kompilatorn, när den träffar på ett värde av en annan typ än den förväntade typen, försöker hitta ett sätt att konvertera det påträffade värdet till ett värde av den förväntade typen. Det är inget konstigt med det, vi är vana vid att int-värden automatiskt konverteras till double-värden när de blandas med double-värden i uttryck och många fler sådana situationer. Så kompilatorn ser Text-konstruktorn som ett sätt att från det påträffade char *-värdet konstruera ett värde av den förväntade Text-typen och kommer att anropa Text-konstruktorn själv. Om man vill förhindra sådan implicit konvertering så kan man deklarera konstruktorn med nyckelordet explicit: class Text{ int siz; char *cptr; explicit Text(char *str){ Bild 63 ; 9

Temporära objekt Uttrycket Text( Jozef ) i funk(text( Jozef )); är ett explicit konstruktoranrop. Det resulterar i att ett temporärt objekt skapas, används och städas bort. Precis samma sak händer vid implicit konvertering. Temporära värden är vi vana vid från t.ex. aritmetiska uttryck, vid beräkningen int sek_per_dygn = 24 * 60 * 60; beräknas först 24 * 60, detta resultat måste lagras någonstans för vidare beräkning. Temporära objekt betraktas på samma sätt: de lever en kort stund och städas bort automatiskt. Om det finns en const-referens som är knutet till objektet så lever objektet så länge referensen finns, annars försvinner det när det uttryck däri det skapats är utvärderat (en referens som inte är const kan inte knytas till ett temporärt objekt). Obs dock att konstruktorn körs som vanligt vid skapandet och destruktorn vid bortstädandet. Bild 64 Temporära objekt, forts. Skapande av temporärt objekt genom explicit konstruktoranrop är ett bekvämt sätt att konstruera ett objekt som behövs bara för att omdelbart kopieras någonstans eller knutas till en const-referens-argument för att existera bara under ett funktionsanrop. class Klocka{ int totmin; Klocka(int min_efter_midnatt){ totmin = min_efter_midnatt; int get_tot_min() { return totmin; ; Klocka operator+(const Klocka& k, int min){ return Klocka(k.get_tot_min() + min); Bild 65 10

Objekttyper per livslängd Statiska objekt Definierade utanför funktioner eller som lokala statiska objekt Ligger i den statiska arean Initieras vid programstarten, finns under hela programexekveringen, destrueras automatiskt när exekveringen terminerar Lokala automatiska objekt Definierade lokalt i funktioner (ev. i inre block). Läggs på stacken. Initieras under anropet av funktionen när exekveringen nått definitionen, finns under anropet (ev. exekveringen av inre blocket), destrueras automatiskt när anropet terminerar (ev. när exekveringen lämnar det inre blocket) Dynamiskt skapade objekt Skapas med new, t.ex Text *tpek=new Text( Dynamo ); Läggs på heapen Finns tills de städats bort med delete, t.ex. delete tpek; Temporära objekt Skapas genom explicit anrop av konstruktorn, t.ex. Text( Tempo ) eller då kompilatorn tycker sig behöva ett sådant objekt Destrueras automatiskt så fort uttrycket det ingick i är klart eller då referensen som knutits till det försvinner Bild 66 Statiska klassmedlemmar Liksom i Java kan man deklarera statiska medlemmar som då blir globalt existerande enheter i en enda uppsättning, tillgängliga för alla objekt av klassen men även då det inte finns något objekt. Statiska medlemmar kan endast deklareras i klassen, de måste definieras utanför (ett undantag utgörs av definitioner av heltalskonstanter). Åtkomst görs genom räckviddsoperatorn :: Det klassiska exemplet - objekträknande klass: class Counted{ static int count; // Endast deklaration string data; // Instansvariabel Counted(string s):data(s){ count++; Counted(const Counted& other):data(other.data){ count++; ~Counted(){ count--; string get_data() const { return data; static int get_count(); // Endast deklaration ; Bild 67 11

Statiska medlemmar, forts. Definition av statiska medlemmar måste göras utanför klassen (i.cpp-filen): int Counted::count=0; // Definition med initiering int Counted::get_count(){ // Definition av medlemsfunktionen, return count; // ordet static upprepas inte Anrop av get_count() görs enligt följande: void funk(){ Counted c1( Jozef ), c2( Stefan ); cout << Counted::get_count() << endl; Statiska medlemmar är inte lika vanligt förekommande som i Java eftersom det i C++ ju finns fristående funktioner - i Java måste alla funktioner tillhöra någon klass vilket leder till missbruk av statiska medlemsfunktioner. I C++ grupperar man klasser och tillhörande hjälpfunktioner i namnrymder. Bild 68 friend-deklarationer Genom att i en klass deklarera en funktion som friend ger man funktionen tillgång till klassens privata medlemmar: class Klocka { int min; friend Klocka operator+(int, const Klocka&); ; const Klocka operator+(int m, const Klocka& k){ return Klocka(m + k.min); Obs! att friend-deklarationen inte deklarerar funktionen, den säger bara att om det finns en sådan funktion och om den försöker göra åtkomst till privata data i denna klass så är det ok. En friend-deklaration introducerar ju inte en medlem, så det spelar ingen roll om den skrivs i en public-, private- eller protected-sektion. Bild 69 12

friend-deklarationer, forts. Om friend-funktionen är en medlemsfunktion i en annan klass anges dess namn som vanligt med klassens namn och deklarationsviddsoperatorn, definitionen av denna klass måste i så fall stå före: class Alfa{ void funk(beta& b) { b.x++; ; class Beta{ int x; friend void Alfa::funk(const Beta&); ; Om man vill ge alla medlemsfunktioner i en annan klass sådan åtkomst deklarerar man klassen som friend, t.ex. friend class Alfa; Om två klasser skall ge varandra ömsesidig tillgång till sina privata delar kan kravet på att definitionen skall stå innan inte uppfyllas, då kan en av klasserna deklareras först: class Alfa; class Beta{ friend class Alfa;. ; class Alfa{ friend class Beta; ; Bild 70 const-ighet :) Att vissa objekt eller värden inte får modifieras (alls eller av ett visst programavsnitt) är en viktig aspekt av programdesign. I C++ finns utbyggt språkligt stöd för detta med kompilatorkontroll. Givet en typ, t.ex. int eller Person, så betraktas typen const int eller const Person som en annan typ, besläktad med den ursprungliga typen men utan modifieringsoperationer. Det är tillåtet med tilldelning från icke-const till const men inte tvärtom. Bild 71 13

const-ighet, forts. Antag class Coord{ int x, y; Coord(int xx, int yy):x(xx), y(yy){ int getx() { return x; int gety() { return y; void move(int dx, int dy) { x+=dx; y+=dy; ; Kompilatorn kontrollerar att man inte har möjlighet att (av misstag) modifiera ett const-objekt: void gunk(coord& a){ // funk med icke-const argument, kan ändra a void funk(const Coord& x){ Coord *pek1 = &x; // kompileringsfel! const Coord *pek2 = &x; // ok gunk(x); // kompileringsfel! gunk(const_cast<coord&>(x)); // ok int y = x.getx(); // kompileringsfel! Bild 72 const-ighet, forts. Det sista felet på föregående bild int y = x.getx(); // kompileringsfel! beror på att medlemsfunktionen getx inte deklarerar att den inte förändrar sitt objekt, därför får man inte anropa den hos ett const-objekt. Att en medlemsfunktion inte förändrar sitt objekt deklareras med ordet const i funktionshuvudet efter argumentlistan: class Coord{ int x, y; Coord(int xx, int yy):x(xx), y(yy){ int getx() const { return x; int gety() const { return y; void move(int dx, int dy) { x+=dx; y+=dy; ; Detta gäller endast icke-statiska medlemsfunktioner. Ordet const här är en del av signaturen, ska alltså stå både i deklarationer och i definitionen. Bild 73 14

const-ighet, forts. Ibland uppstår behovet att ha en viss operation i en klass implementerad på två olika sätt så att den kan utföras på ett sätt i ett const-objekt och på ett annat sätt i ett icke-const-objekt. Man kan då ha två medlemsfunktioner med samma namn och argument men den ena deklarerad som const och den andra inte. Det vanligaste exemplet är indexeringsoperatorn: class Text{ int siz; char *cptr; char& operator[](int index){ return cptr[index]; char operator[] (int index) const { return cptr[index]; ; Hos ett const-objekt kommer const-funktionen att anropas, hos ett icke-const-objekt anropas icke-const-funktionen. Bild 74 const-ighet, forts. Objekt av klasser med värdesemantik ska ibland bete sig som modifierbara objekt, men ibland som icke-modifierbara värden. T.ex. ska ju resultatet av en överlagrad operator+ vara ett värde, inte ett lvalue, givet Klocka k1(13,0), k2(23,0); ska man ju inte kunna göra: k1 + 3*45 = k2; // Borde vara fel! För att ange att ett objekt ska betraktas som värde deklarerar man dess typ som const, t.ex. const Klockslag. T.ex. borde operator+ för Klockslag vara: const Klocka operator+(const Klocka& k, int min){ return Klocka(k.get_tot_min() + min); varvid modifieringar av resultatet förbjuds. Bild 75 15

const-ighet, forts. Vid avancerad utformning av biblioteksklasser kan det hända i mycket speciella situationer att någon medlemmar som inte är synlig för tillämpningsprogrammeraren ska kunna modifieras även i const-objekt. I så fall kan medlemmen deklareras med nyckelordet mutable och kan då modifieras även om dess objekt är const. Långsökt exempel: Text-klass där man samlar indexeringsstatistik: class Text{ int size; char *cptr; mutable int times_indexed; Text():size(0), cptr(0), times_indexed(0){ Text(const char *str); char& operator[] (int index){ times_indexed++; return data[index]; char operator[] (int index) const { times_indexed++; return data[index]; int get_times_indexed() const { return times_indexed; ; Bild 76 16