2D1387, Programsystemkonstruktion med C++ 01/02 1 Del2 Klasser, medlemmar och arv Ämnesområden denna föreläsning: Klasser, åtkomst Medlemmar, medlemsfunktioner, inline Slide 1 Konstruktorer och destruktorer this-pekaren Arv, åtkomst Multipelt arv, virtuell basklass Konstanta funktioner och data, mutable Tips inför labbarna Klasser Klasser används för att Slide 2 strukturera koden kapsla in data knyta metoder till data skapa återanvändbara komponenter
2D1387, Programsystemkonstruktion med C++ 01/02 2 Medlemmar och åtkomst Alla kommer åt public Slide 3 Endast subklasser kommer åt protected Endast klassen själv kommer åt private Medlemsfunktioner som de nieras i klassdeklarationen är implicit deklarerade som inline Slide 4 /* myfile.h -- headerfil med deklarationer */ class A int i; // i är private // foo är inline int foo(char) return 7; } int bar(); // bar, baz är inline om char baz(); // definitionen anger det protected: double d; inline int A::bar() return 2; } // inline i.h-fil /* myfile.cpp -- källkodsfil med definitioner */ char A::baz() return 'x'; } // ej inline i.cpp-fil
2D1387, Programsystemkonstruktion med C++ 01/02 3 Statiska medlemmar Statiska medlemmar är gemensamma för alla instanser av samma klass. De kan t.ex. användas till att räkna instanser av klassen. Slide 5 class A // i headerfil myfile.h static const int fixed = 4711; // statisk medlem static int cnt; // statisk medlem int array[fixed]; // vanlig medlem static int count() return cnt;} // statisk funktion // i implementationsfil myfile.cpp const int A::fixed; // statiska medl. måste definieras int A::cnt; //... och defaultvärde är 0... int i = A::count(); Konstruktorer När ett objekt instansieras anropas dess konstruktor Konstruktorer saknar returtyp En implicit konstruktor tillhandahålls av kompilatorn vid behov Slide 6 Konstruktorer kan överlagras Använd explicit då konstruktorn har ett argument av enkel typ Konstruktor som är protected ger klass som bara kan skapas av subklasser Konstruktor som är private ger möjlighet att förhindra användandet av t.ex. kopieringskonstruktorn
2D1387, Programsystemkonstruktion med C++ 01/02 4 Slide 7 // date.h -- headerfil class Date Date(); // privat defaultkonstr. Date(const Date &); // kopieringskonstruktor Date(int y, int m, int d); // ÅÅMMDD explicit Date(int c); // dagar från 1900-01-01... int year, month, date; // date.cpp -- källkodsfil Date::Date(const Date &d) /* kopiering */ } Date::Date(int y, int m, int d) : year(y), month(m), date(d) // initiering av medl. } class A A(int i) : number(i) } int number; // konstruktor Slide 8 class B : public A B(double d, int i) : A(i), value(d) } // initiera basen double value;
2D1387, Programsystemkonstruktion med C++ 01/02 5 Destruktorer När ett objekt förstörs (t.ex. med delete eller då det hamnar utanför räckvidd) anropas dess destruktor Slide 9 Destruktorn i en basklass bör vara deklarerad virtual Destruktorn har ingen returtyp Syntaxen för destruktorn A() kommer från operatorn för bitvis komplement ( ) Slide 10 class String // ligger i string.h -- headerfil String() : str(0) } // defaultkonstruktor String(const char *s); // kopierar s ~String(); // destruktor protected: char *str; // string.cpp -- källkodsfil String::String(const char *s) : str(0) str = new char[strlen(s) + 1]; // glöm inte '\0' strcpy(str, s); } String::~String() delete [] str; } // obs! delete[]
2D1387, Programsystemkonstruktion med C++ 01/02 6 this-pekaren I en medlemsfunktion kan man komma åt objektet genom att använda den förde nierade pekaren this. this går inte att tilldela. Slide 11 class MyInt int i; MyInt &increase(int); inline MyInt &MyInt::increase(int j) i += j; return *this; // ger referens till objektetet självt } Arv och aggregation För att återanvända be ntliga objekt nns olika möjligheter: Aggregat (innehållande), har en Arv, är en Olika typer av arv är public, protected och private Slide 12 class Animal String species; // Animal innehåller (har en) sträng class Bear : public Animal // Bear ärver (är en) Animal...
2D1387, Programsystemkonstruktion med C++ 01/02 7 Slide 13 class Vehicle Vehicle(int w) : weight(w) } int weight; class Car : public Vehicle Car(int p, int w) : Vehicle(w), persons(p) } int persons;... Car volvo(5, 1700); int i = volvo.weight; Åtkomst vid arv Klasser som är till hjälp för implementationen bör ärvas protected eller private. Man vill inte ha implementationsdetaljer i sitt gränssnitt. Slide 14 Typ av arv public Åtkomst på data/funktion public protected private public protected protected protected protected private private private
2D1387, Programsystemkonstruktion med C++ 01/02 8 Multipelt arv C++ stöder multipelt arv av godtyckligt antal klasser. Slide 15 class Boat Boat(bool s) : has_sail(s) } bool has_sail; class Amphibian : public Car, public Boat Amphibian(int pers, int wt, bool sail = false) : Car(pers, wt), Boat(sail) } Virtuell basklass För att använda gemensamma data i en delad basklass använder man virtuella basklasser genom nyckelordet virtual. Slide 16 A A A B C B C D D
2D1387, Programsystemkonstruktion med C++ 01/02 9 Ett exempel på hur data kan delas mellan basklasserna: class A int i; class B : public A class C : public A class D : public B, public C Slide 17 class Bv : virtual public A class Cv : virtual public A class Dv : public Bv, public Cv Dv d; d.a::i = 7; d.bv::i = 8; d.cv::i = 9; // alla ändrar samma data Slide 18 D e; e.a::i = 7; e.b::i = 8; e.c::i = 9; // fel: tvetydigt // ok: ändrar D->B->A1 // oj, ändrar D->C->A2
2D1387, Programsystemkonstruktion med C++ 01/02 10 Konstanta funktioner och variabler Deklarationer med const ger skrivskydd. Slide 19 class A const int ci; const int *p_ci; int *const cp_i; const int *const cp_ci; // ci är skrivskyddad (ssk) // pekare till ssk int // ssk pekare till int // ssk pekare till ssk int int get() const; // objektet är ssk i get int i; // källkodsfil int A::get() const return i; } // ok: förändrar inte int A::get() const return i = 2; } // fel: förändrar obj. mutable ändrar i objekt som är const Funktioner deklarerade med const gör objektet read-only. Med mutable inför man ett undantag. Slide 20 class A int get() const; // objektet är read-only i get int i; mutable int access_count; inline int A::get() const access_count++; return i; } // ok: får ändras trots const
2D1387, Programsystemkonstruktion med C++ 01/02 11 Några tips inför labbarna att spara tid Minska utveckligstiden: Minimera antal beroenden mellan lerna Använd make. Automatisering lönar sig alltid! Använd debuggern DDD (gdb) i alla labbar Slide 21 Undvik att redovisa mer än en gång: Använd const på rätt sätt Bryt ut gemensam funktionalitet och lägg i rätt klass. Tänk på vad som hör/inte hör hemma i basklassen Undvik att läcka minne Anmäl din redovisning i god tid! Omredovisning ska ske innan deadline. Var gärna redovisningsklar innan sista datumet. Labb2 kalender Börja i tid Slide 22 Undvik aritmetiskt huvudbry: tabellera starto set för de år din kalender ska hantera Lägg inga medlemsvariabler eller konstanter (t.ex. namn på månad) i basklassen (Hur passar en marsiansk kalender in?) Testa din kalender på len date.cpp och visa vid redovisning
2D1387, Programsystemkonstruktion med C++ 01/02 12 Labb 2 extrauppgift 2.2 En intressant idé: Cal::Calendar c; Menu::Output out_new_person("add a new person"); Menu::Output out_new_shoe("add a new shoe"); Menu::Output out_shoe_menu("shoe menu"); Slide 23 Menu::Input inp_age("enter the age: "); Menu::Input inp_name("enter the name: "); Menu::Input inp_size("enter the shoe size: "); Menu::Event2<...> person_event(out_new_person, inp_age, inp_name, c, &Cal::Calendar::person); Menu::Event1<...> shoe_event(out_new_shoe, inp_size, c, &Cal::Calendar::shoe); Menu::Submenu second_menu("shoe menu:"); second_menu.add(out_new_shoe, shoe_event); Slide 24 Menu::Submenu first_menu("main menu:"); first_menu.add(out_new_person, person_event); first_menu.add(out_shoe_menu, second_menu); Med arv, mallar och STL: 150 rader kod och mycket exibelt main menu: [0] add a new person [1] shoe menu [q] quit
2D1387, Programsystemkonstruktion med C++ 01/02 13 Projektuppgift äventyrsspel Börja i tid! Lägg inga specialiserade funktioner (såsom Wizard::magic) i basklasserna Character, Object och Environment Slide 25 Använd mallar och arv itigt för att lättare bryta ut gemensam funktionalitet Uppgift 3.2 : Tänk på att objekten har xerad ordning (gäller ej extrauppgift 3.2), vilket underlättar laddningen avsevärt. Lägg aktörer, objekt och miljöer i varsin My_vector. Extrauppgift 3.2 : Skapa en mallfunktion som returnerar en pekare till ett nytt objekt. Låt objektets konstruktor ta dataströmmen som argument. På så sätt blir varje klass ansvarig för att initiera sig själv. Beroenden För att minska kompileringstiderna vill man ha så få beroenden som möjligt mellan ler: Slide 26 Undvik att inkludera onödiga ler i dina header ler. Undvik att använda inline på funktioner som använder andra objekt eftersom deras de nition då måste vara synlig. Använd framåtdeklaration där detta är möjligt.
2D1387, Programsystemkonstruktion med C++ 01/02 14 Framåtdeklaration När kan man använda framåtdeklaration av en klass och när måste de nitionen vara synlig? De nitionen måste vara synlig för alla basklasser. Slide 27 De nitionen måste vara synlig då man de nierar objekt, t.ex. som medlem. De nitionen måste vara synlig då man använder ett objekt, t.ex. tilldelar en av objektets medlemmar. De nitionen behöver ej vara synlig i övriga fall! Exempel: pekare eller referens till objekt, returtyp i deklaration av funktion, argument i deklaration av funktion eller när man använder typen i typdeklaration eller som malltyp Exempel på framåtdeklaration och inklusion av header ler: // i filen A.h #include "D.h" // inkludera D:s definition Slide 28 class B; // (framåt)deklaration av B class C; // (framåt)deklaration av C class A // definition av klassen A B foo(b); // B behöver ej vara definierad! C *bar(); // C behöver ej vara definierad B &b; // B behöver ej vara definierad D c; // ok: D är definierad B b; // fel: B måste vara definierad vector<b> v; // B behöver ej vara definierad
2D1387, Programsystemkonstruktion med C++ 01/02 15 // i filen A.cpp #include "A.h" #include "B.h" extern C *get_cp(); // A måste vara definierad // B måste definieras // eftersom foo() returnerar B // get_cp definierad i annan fil Slide 29 B A::foo(B) return B(); // B måste vara definierad } C *A::bar() C *p = get_cp(); // returnerar pekare till C return p; // C behöver ej vara definierad } // om vi inte använder C make- ler Använd separata objekt ler och make för att minska kompileringstiderna. I len Makefile eller makefile: Slide 30 CC = g++ # kompilator FLAGS = -g -Wall # flaggor SRCS = main.cpp julian.cpp gregorian.cpp OBJS = main.o julian.o gregorian.o date: $(OBJS) $(CC) $(FLAGS) -o date $(OBJS) %.o: %.cpp $(CC) $(FLAGS) -c $*.cpp depend: makedepend -- $(FLAGS) -- $(SRCS)
2D1387, Programsystemkonstruktion med C++ 01/02 16 Nu kan vi kompilera de olika lerna separat. Efter kompilering länkas objekt lerna till en exekverbar l. Slide 31 datan> make g++ -g -Wall -c julian.cpp g++ -g -Wall -o date main.o julian.o gregorian.o datan> date När beroendena har ändrats, kör make depend. Detta uppdaterar make len och lägger till en lista med beroenden (sist i len): # DO NOT DELETE main.o: julian.h date.h gregorian.h /usr/include/assert.h julian.o: julian.h date.h gregorian.o: gregorian.h julian.h date.h