Vad är en klass? Övnin 2 Daens ämne är klasser och hur de hanteras. Vi kommer att bya upp ett exempel stevis och illustrera en hel del möjliheter, men också problem och saker man bör vara vaksam på. ffl En klass bör fåna ett koncept. Animal () : name ( "no name" ) f Animal ( char Λ n ) : name ( n ) f ffl En klass kan inå ienklasshierarki och specialisera eenskaper hos basklasserna. class Man : Animal f class XMan : Man f Konstruktorer Konstruktorer initierar ett minnesutrymme så att det representerar en instans av en klass. ffl default-konstruktorn tar ina arument och kan se ut på tvåsätt: Animal(); Animal(std::strin name = ""); Den svarar mot ett oinitialiserat objekt, tex ett namnlöst djur. Det år att beränsa användninen av en konstruktor enom att deklarera den protected eller private. ffl copy-konstruktorn tar en instans av klassen som parameter och skapar en kopia. class Anthill f Anthill ( char Λ ascii_pic ) f pic = new char [ strlen ( ascii_pic ) +1]; strcpy ( pic, ascii_pic ); Anthill ( const Anthill &ah ) f pic = new char [ strlen ( ah. pic )+1]; strcpy ( pic, ah. pic ); Anthill () f delete [] pic ; char Λ pic ; Det är ofta nödvändit för klasser som har pekare som medlemmar. Copy-konstruktorn ersätter memberwise initialization, vilket annars sker automatiskt.
När allokeras på stack respektive heap? ffl ytterliare konstruktorer tar en eller flera parametrar och initierar instansen med dessa. Animal () : name ( "no name" ) f Animal ( char Λ n ) : name ( n ) f cout << "Animal()" << endl ; Ofta finns det ett antal olika konstruktorer för olika ändamål. Nåra av dessa kan vara deklarerade protected och endast avsedda för underklasser. Det finns två typer av allokerin. ffl Stacken: allokerin och deallokerin sker automatiskt för temporära variabler. int foo(int i) { int x; Animal a("fido");... return 0; } Vid anropet till foo skapas ett utrymme påstacken för i, x och a. Sedan körs konstruktorn i klassen Animal för att initiera a. Vid return körs destruktorn för a och sedan betraktas det allokerade utrymmet som fritt att använda. Utrymmet kan fysiskt innehålla samma information, men det kan skrivas över i vilket öonblick som helst. ffl Heapen: prorammeraren tar över ansvaret för deallokerin. Animal* bar(int size) { Animal *a = new Animal("Fido"); int *x = new int[size];... delete [] x; return a; } Till skillnad från stacken kan objekt på heapen överleva funktionsanrop. Det är nåot lånsammare att allokera på heapen, men framför allt så är det mycket lätt att öra fel och få minnesläckor. Var noranna vid allokerin på heapen och fundera på hur ni ska deallokera objekt som ni allokerar på heapen. Vad händer när... en instans skapas: 1. Allokera ett minnesutrymme som är stort no för objektet på stacken eller på heapen. 2. Kör konstruktor för superklass. 3. Kör konstruktorer för medlemsvariabler. 4. Kör konstruktorn....eller raderas: 1. Kör destruktorn. 2. Kör destruktorn för medlemsvariabler. 3. Kör destruktorn för superklass. 4. Deallokera minnesutrymmet.
Varför en initierinslista? ffl const respektive referens deklarerade medlemmar måste initieras i initierinslistan. Varför? class Ant : public Animal f Ant ( char Λ n, Anthill &ah, const bool b ) : Animal ( n ), anthill ( ah ), worker ( b ) f // anthill = ah ; // ERROR! // worker = b ; // ERROR! const bool worker ; Anthill &anthill ; ffl Initierinslistan är ofta effektivare då den undviker exekverin av en defaultkonstruktor. Animal ( char Λ n ) : name ( n ) f Använd initierinslistan om det år! Heap-allokerin i klass och Destruktorn En klass behöver ofta allokera minne på heapen internt. Detta örs i konstruktorn och destruktorn deallokerar sedan minnet. class Anthill f Anthill ( char Λ ascii_pic ) f pic = new char [ strlen ( ascii_pic ) +1]; strcpy ( pic, ascii_pic ); Anthill () f delete [] pic ; char Λ pic ; Om vi nu stack-allokerar ett Anthill-objekt så behöver vi inte bry oss om deallokerinen, för det har vi redan löst en ån för alla!! int foo () f Det är prorammerarens ansvar att deallokera!
Åtkomstberänsnin Det finns tre nivåer för åtkomst till medlemmar i en klass. ffl public er alla tillån till medlemmarna. ffl protected er deriverade klasser tillån till medlemmarna. ffl private beränsar tillånen till klassen själv. protected : friend ostream& operator<<(ostream&, Animal &); Vid arv kan åtkomstberänsninen stärkas, men inte försvaas. class A f class B : public A f class C : protected A f class D1 : private A f class D2 : A f En klass kan bjuda in en speciell funktion eller klass att få tillån till dess medlemmar enom att deklarera den friend. Arv: konstruktorer Animal ( char Λ n ) : name ( n ) f cout << "Animal()" << endl ; virtual Animal () f cout << " Animal()" << endl ; class Man : public virtual Animal f Man ( char Λ n ) : Animal ( n ) f cout << "Man()" << endl ; Man () f cout << " Man()" << endl ; class XMan : public Man f XMan ( char Λ n ) : Animal ( n ), Man ( n ) f cout << "XMan()" << endl ; XMan () f cout << " XMan()" << endl ; XMan x ( "Maneto" ); Detta er vid körnin $./example1 Animal() Man() XMan() XMan() Man() Animal()
Arv: multipelt arv Vi blandar myra med människa :-) ; class Ant : public virtual Animal f Ant ( char Λ n, Anthill &ah, const bool b ) : Animal ( n ), anthill ( ah ), worker ( b ) f char Λ draw () f return "\\o/\n" "-o-\n" "/O\\\n" ; const bool worker ; Anthill &anthill ; class Man : public virtual Animal f Man ( char Λ n ) : Animal ( n ) f char Λ draw () f return " o \n" "- -\n" " /\\ \n" ; class Monster : public Man, public Ant f Monster ( char Λ n, Anthill &ah ) : Animal ( n ), Man ( n ), Ant ( n, ah, false ) f Utan virtual blir det två kopior av Animal som måste åtskiljas vid anrop. Monster m ( Monstermannen, ah ); m. Man ::draw (); Nyckelordet virtual ör att: ffl det bara skapas en kopia av superklassen Animal. ffl den understa underklassen måste själv initiera Animal. (detta är inte tillåtet i annat fall) Polymorphism Betrakta raderna: virtual char Λ isa () f return "Animal" ; class Ant : public virtual Animal f char Λ isa () f return "Ant" ; class Man : public virtual Animal f char Λ isa () f return "Man" ; class XMan : public Man f char Λ isa () f return "XMan" ; class Monster : public Man, public Ant f char Λ isa () f return "Monster" ; En av fördelarna med objektorienterin är att man kan öra följande: Animal Λ ani [4]; ani [0] = new Ant ( "Lars", ah, true ); ani [1] = new Man ( "Stefan" ); ani [2] = new XMan ( "Maneto" ); ani [3] = new Monster ( "Antman", ah ); for ( int i = 0; i < 4; i++) f cout << Λani [ i] << " is a " << ani [ i] >isa () << endl ; for ( int i = 0; i < 4; i++) f delete ani [ i ];
ffl Notera att Animal::isa() är deklarerad virtual. ffl Kompilatorn håller i en tabell reda på vilken class en instans tillhör eentlien. Vid anrop örs ett uppsla i tabellen. Detta funerar med både pekare och med referenser, men inte med vanlia variabler. ffl Viktit: Om vi inte hade Animal:: Animal() som virtual så hade vi råkat ut för en minnesläcka med koden ovanför. Varför? Destruktorer i basklasser ska vara virtual! ffl IJavaär alla klassfunktioner motsvariheten till virtual om inet speciellt örs. I java kan man använda nyckelordet final för att häva detta och framtvina statisk länknin. Abstrakta basklasser Animal borde vara en abstrakt klass som aldri själv instansieras, men som beskriver emensamma attribut hos alla djur. Det är tex oklart vad Animal::isa() ska returnera. Ett alternativ är att definiera: virtual char Λ isa () = 0; Det innebär att det inte år att instansiera Animal, samt att varje underklass U måste definiera en funktion U::isa(). DvsAnimal blir abstrakt. Animal::isa() kallas nu en strikt virtuell funktion. Static-variabler och medlemmar Anta att vi vill hålla koll på hur måna myror det finns vid varje tillfälle. Ett naturlit sätt att öra det är följande. class Ant : public virtual Animal f static int et_no_ants () f return no_ants ; Ant ( char Λ n, Anthill &ah, const bool b ) : Animal ( n ), anthill ( ah ), worker ( b ) f Ant ::no_ants++; Ant () f Ant ::no_ants ; static int no_ants ; const bool worker ; Anthill &anthill ; // Next line can not be i. h file, WHY? // It must appear exactly once. int Ant ::no_ants = 0; Stroustrup säer: Operatoröverlarin It s hard to overestimate the importance of concise notation for common types.. Av detta skäl så kanmanic++överlara inte bara alla vanlia funktioner utan även inbyda funktioner som tex +,-,*,++ osv Ivårt exempel överlarade vi << operatorn. Ett bra tillfälle att överlara operatorer är när man implementerar aritmetik med komplexa tal tex. Då är det trevlit att kunna skriva: Complex a (1,2); Complex b (4,5); Complex c ; c = a + b ;
Hela exemplet Två varninar ffl Använd operatoröverlarin endast då detär fullt naturlit! Alla dataloer tycker inte som Stroustrup. ffl Använd multipelt arv ytterst sparsmakat.det är lätt att öra fel, och det finns ofta alternativa sätt att lösa problem på somär bättre. Måna tycker att multipelt arv är en styelse. En snyare lösnin av problemet återfinns i Java där man infört bereppet interface. En klass får bara ärva en klass, men kan implementsera hur måna interface som helst. Fil 1: someclasses.h #ifndef SOMECLASSES #define SOMECLASSES #include <iostream> #include <stdlib. h> #include <strin> friend ostream& operator<<(ostream&, Animal &); Animal () : name ( "no name" ) f Animal ( char Λ n ) : name ( n ) f cout << "Animal()" << endl ; virtual Animal () f cout << " Animal()" << endl ; virtual char Λ isa () = 0; class Anthill f friend ostream& operator<<(ostream &os, Anthill &a ); Anthill ( char Λ ascii_pic ) f pic = new char [ strlen ( ascii_pic ) +1]; strcpy ( pic, ascii_pic ); cout << "Anthill() allocates" << endl ; Anthill ( const Anthill &ah ) f pic = new char [ strlen ( ah. pic )+1]; strcpy ( pic, ah. pic ); Anthill () f delete [] pic ; cout << " Anthill() deallocates\n" ; char Λ pic ; class Ant : public virtual Animal f static int et_no_ants () f return no_ants ; Ant ( char Λ n, Anthill &ah, const bool b ) : Animal ( n ), anthill ( ah ), worker ( b ) f cout << "Ant()" << endl ; Ant ::no_ants++; Ant () f cout << " Ant()" << endl ; Ant ::no_ants ; char Λ isa () f return "Ant" ; char Λ draw () f cout << "\\o/\n" "-o-\n"
"/O\\\n" ; static int no_ants ; const bool worker ; Anthill &anthill ; class Man : public virtual Animal f Man ( char Λ n ) : Animal ( n ) f cout << "Man()" << endl ; Man () f cout << " Man()" << endl ; char Λ isa () f char Λ draw () f cout << " o \n" "-U-\n" " /\\ \n" ; return "Man" ; class XMan : public Man f XMan ( char Λ n ) : Animal ( n ), Man ( n ) f cout << "XMan()" << endl ; XMan () f char Λ isa () f cout << " XMan()" << endl ; return "XMan" ; class Monster : public Man, public Ant f Monster ( char Λ n, Anthill &ah ) : Animal ( n ), Man ( n ), Ant ( n, ah, false ) f cout << "Monster()" << endl ; Monster () f cout << " Monster()" << endl ; char Λ isa () f return "Monster" ; #endif // SOMECLASSES Fil 2: someclasses.cpp ostream& operator<<(ostream & os, Animal &a ) f os << a. name ; return os ; ostream& operator<<(ostream & os, Anthill &ah ) f os << ah. pic ; return os ; int Ant ::no_ants = 0; Fil 3: example1.cpp int main () f XMan x ( "Maneto" ); Fil 4: example2.cpp " xxxxxxxxxxxxxxxxx" ); cout << "\na nice anthill:\n" << ah << endl << endl ; Fil 5: example3.cpp
Monster m ( "Monstermannen", ah ); m. Man ::draw (); m. Ant ::draw (); Fil 6: example4.cpp Ant a1 ( "Mats", ah, true ); Ant a2 ( "Lars", ah, true ); Ant a3 ( "Ulrika", ah, true ); Ant a4 ( "Anna", ah, true ); cout << "Number of ants is: " << Ant ::et_no_ants () << endl ; Fil 7: example5.cpp Animal Λ ani [4]; ani [0] = new Ant ( "Lars", ah, true ); ani [1] = new Man ( "Stefan" ); ani [2] = new XMan ( "Maneto" ); ani [3] = new Monster ( "Antman", ah ); for ( int i = 0; i < 4; i++) f cout << Λani [ i] << " is a " << ani [ i] >isa () << endl ; for ( int i = 0; i < 4; i++) f delete ani [ i ];