KAPITEL 8 Klasser Det är egentligen nu som kursen i programmeringsteknik börjar..., s k objektorienterad programmering. 169 Hittills: Enkel datatyp: double, int, char Operationer: förutbestämda, t ex +, -, *, /, %. Arrayer (fält): Gruppering av element med samma sorts datatyp (enkel eller array). Operationer: kan indexeras. Nu: Klasser: Gruppering av element med lika eller olika sorters datatyper, t ex enkla, arrayer, andra klasser. Operationer: programmeraren bestämmer dessa själv genom att skriva speciella funktioner för klassen. Början till s k objektorienterad programmering. En klass är en mängd av s k objekt. Ex. Antag vi vill definiera en datatyp som beskriver en vara i en affär. Frågor: 1) Hur skall varan representeras? 2) Vad vill vi kunna göra med varan? Svar 1): T ex kan varan karaktäriseras av Namn Pris Antal (i lager) (sträng) (double) (int) Vi låter klassen heta Vara. De ingående delarna i klassen kallas för (data)attribut eller datamedlemmar. Vi skall visa att man kan tillverka varor, dvs deklarera några variabler att ha datatypen Vara. Dessa variabler är då objekt i klassen Vara. 170 171
Svar 2): Vi vill kunna göra följande med en vara (dvs en variabel av typen Vara): Ge varan initialvärden, t ex läsa in data om en ny vara. Få hem nya varor av samma sort, dvs addera varor till lagret. Sälja varor, dvs ta bort varor ur lagret. Sätta nytt pris på varan, givet en procentfaktor. Skriva ut information om en vara. Vi har nu följande grafiska beskrivning på ett objekt i klassen Vara: Vara char Namn[100] double Pris int Antal Klassens namn Attribut (data i klassen) Centralt i objektorienterad programmering: Operationer på ett objekt definieras som s k medlemsfunktioner (metoder) i klassen. LaesIn TillLager Saelja ProcentPris SkrivUt Medlemsfunktioner (operationer på data) Punkterna ovan kommer att representeras av medlemsfunktionena: LaesIn, TillLager, Saelja, ProcentPris, SkrivUt 172 173 Klassdeklaration i C++: class Klassnamn deklarationer av gömda attribut deklarationer av synliga medlemsfunktioner ; Man låter data vara private. Innebär att ingen utanför klassen kan komma åt dessa datafält. Enbart klassens egna funktioner får använda dessa. Alla data kan kommas åt direkt från alla medlemsfunktioner utan parameteröverföring. Man låter medlemsfunktioner vara public. Innebär att de kan kommas åt utifrån, t ex från main. Sammanfattning: Objektorientering innebär att uppmärksamheten riktas på objekt inom det problemområde uppgiften gäller. Uppgiften löses genom samverkan mellan olika objekt. Programmeringen består i att definiera objekt och deras samverkansformer. Inkapsling: objekten innehåller både data och beteende (funktioner som gör något med data). En användare behöver inte veta hur en vara t ex representeras, bara vilka operationer som finns. Gömmer representationen av data ==> Flexibelt: kan ändra representationen av data men ofta ha samma huvudprogram kvar!(om ej funktionshuvudena i metoderna ändrats). ---------------------------- 174 175
// Vara1_alla.cc #include <iostream.h> #include <string.h> //Klassdeklaration för klassen Vara. class Vara char Namn[100]; double Pris; int Antal; void LaesIn() cout << "Ge varans namn:" << endl; cin.getline( Namn, sizeof( Namn ) ); cout << "Ge pris och antal i lager:" << endl; cin >> Pris >> Antal; cin.get(); //Så man kan läsa in ny vara senare.. void TillLager( int AntalNya ) //Nya till lagret. Antal += AntalNya; void Saelja( int AntalSaelj ) //Sälj vara. Antal -= AntalSaelj; void ProcentPris( double Faktor ) //Sätt nytt pris. void SkrivUt() //Skriv info om vara. cout << "\t Varunamn: " << Namn << "\t Pris: " << Pris << "\t Antal i lager: " << Antal << endl; ; //OBS! Semikolon efter slutklammern! 176 177 //================================================ // Huvudprogram som testar klassen Vara. Vara Mjoelk; Mjoelk.LaesIn(); Mjoelk. //Skapar obkektet Mjoelk. //Laes in data till Mjoelk-objektet. //Skriv ut info om varorna. Mjoelk.ProcentPris( 1.10 ); //Prisökning 10%. Mjoelk.TillLager( 100 ); // 100 nya från grossisten. Mjoelk.Saelja( 50 ); // Sälj 50 st Mjoelk. //Skriv ut info om varorna. return 0; //...slut på filen Vara1_alla.cc... För att få ett välstrukturerat och överblickbart program: Klassdeklaration först. main. definition (implementation) av metoder sist. OBS! Vara:: nedan anger klasstillhörighet. Olika klasser kan ha samma metodnamn, t ex vanligt med LaesIn, SkrivUt... $ a.out Ge varans namn: Mjölk Ge pris och antal i lager: 7.15 12000 Varunamn: Mjölk Pris: 7.15 Antal i lager: 12000 Varunamn: Mjölk Pris: 7.865 Antal i lager: 12050 Senare kommer vi att dela upp dessa programdelar på olika filer, dvs deklarationen på en fil, implementationen på en och main på en. 178 179
//=============================================== // Vara1.cc, variant av Vara1_alla.cc // #include <iostream.h> #include <string.h> //Klassdeklaration för klassen Vara. class Vara char Namn[100]; double Pris; int Antal; void LaesIn(); void TillLager( int AntalNya ); void Saelja( int AntalSaelj ); void ProcentPris( double Faktor ); void ; //Nya till lagret. //Sälj vara. //Sätt nytt pris. //Skriv info om vara. //================================================= // Huvudprogram som testar klassen Vara. Vara Mjoelk; Mjoelk.LaesIn(); Mjoelk. //Skapar obkektet Mjoelk. //Laes in data till Mjoelk-objektet. //Skriv ut info om varorna. Mjoelk.ProcentPris( 1.10 ); //Prisökning 10%. Mjoelk.TillLager( 100 ); //100 nya från grossisten Mjoelk.Saelja( 50 ); //Sälj 50 st. Mjoelk. //Skriv ut info om varorna. return 0; //================================================= // Implementation (definition) av metoder i klassen Vara. void Vara::LaesIn() cout << "Ge varans namn:" << endl; cin.getline( Namn, sizeof( Namn ) ); cout << "Ge pris och antal i lager:" << endl; cin >> Pris >> Antal; cin.get(); //Saa man kan laesa in ny vara senare... 180 181 void Vara::TillLager( int AntalNya ) Antal += AntalNya; void Vara::Saelja( int AntalSaelj ) Antal -= AntalSaelj; void Vara::ProcentPris( double Faktor ) void Vara::SkrivUt() cout << "\t Varunamn: " << Namn << "\t Pris: " << Pris << "\t Antal i lager: " << Antal << endl; //...slut på filen Vara1.cc... Konstruktorer, selektorer och destruktorer: När man deklarerar vanliga variabler (enkla, arrayer) så kan man om man vill initiera dem samtidigt, t ex int sum = 0; double A[ 3 ] = 1.0, 3.7, -5.24 ; Objekt initieras med hjälp av s k konstruktorer. dessa är metoder med samma namn som klassen själv. 1) Parameterlös konstruktor kallas standardkonstruktor eller default-konstruktor. Anropas automatiskt vid deklaration av objekt, t ex Vara Mjoelk; //Här anropas standardkonstr automatiskt!... Definition: Vara::Vara() strcpy( Namn, ); Pris = 0.0; Antal = 0; // Tilldela tom sträng till Namn // Sätt Pris till 0 Kr // Inga varor i lager ännu. 182 183
2) Extrakonstruktor eller överlagrad konstruktor, dvs en konstruktor med en eller flera inparametrar. Man kan ha flera extrakonstruktorer (om alla dessa har olika antal parametrar) T ex Vara Mjoelk( Mjölk, 6.75, 15000 ); //Deklarera och initiera! Vara Franska; // Eller använd extrakonstruktor så här: Franska = Vara( Småfranska, 1.50, 45000 );... Definition: Vara::Vara( char innamn[], double inpris, int inantal ) strcpy( Namn, innamn ); Pris = inpris; Antal = inantal; Selektorer används för att få ut attributvärden (de var ju annars private -deklarerade, dvs gömda i klassen), t ex: Vara Mjoelk; Mjoelk.LaesIn(); cout << Antal i lager just nu: << Mjoelk.HaemtaAntal() << endl;... Definition av selektorer: double Vara::HaemtaPris() return Pris; int Vara::HaemtaAntal() return Antal; void Vara::HaemtaNamn( char utnamn[] ) strcpy( utnamn, Namn ); 184 185 Destruktorn i en klass har som enda uppgift att döda objekt. När man arbetar med dynamiskt allokerat minnesutrymme, så är det viktigt att frisläppa minnesutrymme som inte längre behövs. Om t ex en klass har dynamiska attribut (skapade med new i konstruktorerna), så bör man ha en destruktor som gör delete. Program utan pekare behöver ingen destruktor. Destruktorn, som är parameterlös, har samma namn som klassen, men med ett ~ tecken framför: Vara::~Vara() cout << Hej! Nu dödar jag ; Användandet av metoder (medlemsfunktioner): Utanför klassen, t ex i main, vanlig funktion eller metod i annan klass: objektnamn.metodnamn( ev. parametrar ); Ex. Vara ost; ost.laesin(); I någon annan av medlemsfunktionerna i samma klass: metodnamn( ev. parametrar); Ex. void Vara::ProcentPris( double Faktor ) // Skriv ut info om varan // innan priset ändras. 186 187
Användandet av attribut: Enbart i metoder i samma klass. Om aktuellt objekt så ingen punktnotation, men om deklarerad som lokal variabel eller i parameterlista så punktnotation: objektnamn.attributnamn; Ex. void Vara::KollaSammaPris( Vara annan ) if ( annan.pris == Pris ) cout << samma pris! << endl; Selektorer skickar ut attributvärden till världen utanför klassen. Exempel på ett program (med konstruktorer, destruktor och selektorer) som har en array av objekt i huvudprogrammet: //-----------------Varaenkel_array_i_main.cc #include <iostream.h> #include <string.h> //Klassdeklaration för klassen Vara. class Vara char Namn[100]; double Pris; int Antal; Vara(); //Standardkonstruktor. Vara( char innamn[], double inpris, int inantal ); //Annan konstruktor. ~Vara(); //Destruktor. double HaemtaPris(); //Selektorer: int HaemtaAntal(); void LaesIn(); void //Skriv info om vara. void TillLager( int AntalNya ); //Nya till lagret. void Saelja( int AntalSaelj ); //Sälj vara. void ProcentPris( double Faktor ); //Sätt nytt pris. ; 188 189 //================================================= // Huvudprogram som testar klassen Vara. // Skapa nu en hel array av objekt i klassen Vara. // Antag max st varor. const int MaxVaror = 5; int AntalVaror; Vara Artiklar[ MaxVaror ]; //Här används autom. //standardkonstruktorn 5 ggr! Artiklar[ 0 ] = Vara( Mjölk, 6.50, 3000 ); Artiklar[ 1 ] = Vara( Småfranska, 1.00, 150 ); Artiklar[ 2 ] = Vara( Äpple, 2.00, 7000 ); Artiklar[ 3 ].LaesIn(); AntalVaror = 4; cout << Vi skall sätta om priset på alla varor <<, ge faktorn: ; double Fakt; cin >> Fakt; for ( i = 0; i < AntalVaror; i++ ) Artiklar[i].ProcentPris( Fakt ); cout << Skriver ut varor med nya priser: << endl; for ( i = 0; i < AntalVaror; i++ ) Artiklar[i]. return 0; cout << Nu har vi följande varor i lager: << endl; for ( int i = 0; i < AntalVaror; i++ ) Artiklar[i]. 190 191
//================================================= //Implementation (definition) av metoder i klassen Vara. Vara::Vara() strcpy( Namn, ); //Tom sträng. Pris = 0.0; Antal = 0; cout << Hej default ; cout << endl; Vara::Vara( char innamn[], double inpris, int inantal ) strcpy( Namn, innamn ); Pris = inpris; Antal = inantal; cout << Hej överlagrad ; cout << endl; Vara::~Vara() cout << Hej då! ; void Vara::TillLager( int AntalNya ) Antal += AntalNya; void Vara::Saelja( int AntalSaelj ) if ( AntalSaelj <= Antal ) Antal -= AntalSaelj; else cout << Går ej! Det finns bara << Antal << st << Namn << i lager nu! << endl; void Vara::ProcentPris( double Faktor ) void Vara::SkrivUt() cout << \t Namn: << Namn << \t Pr: << Pris << \t Ant: << Antal << endl; 192 193 void Vara::LaesIn() cout << Ge varans namn: << endl; cin.getline( Namn, sizeof( Namn ) ); cout << Ge pris och antal i lager: << endl; cin >> Pris >> Antal; cin.get(); //Saa man kan laesa in ny vara //senare utan returnteckenkraangel.. Körning: Hej överlagrad Namn: Mjölk Pr: 6.5 Ant: 3000 Hej överlagrad Namn: Småfranska Pr: 1 Ant: 150 Hej överlagrad Namn: Äpple Pr: 2 Ant: 7000 Nu har vi följande varor i lager: Namn: Mjölk Pr: 6.5 Ant: 3000 Namn: Småfranska Pr: 1 Ant: 150 Namn: Äpple Pr: 2 Ant: 7000 Namn: banan Pr: 34 Ant: 250000 Vi skall sätta om priset på alla varor, ge faktorn: 0.50 Skriver ut varor med nya priser: Namn: Mjölk Pr: 3.25 Ant: 3000 Namn: Småfranska Pr: 0.5 Ant: 150 Namn: Äpple Pr: 1 Ant: 7000 Namn: banan Pr: 17 Ant: 250000 Hej då! Namn: Äpple Pr: 2 Ant: 7000 Hej då! Namn: Småfranska Pr: 1 Ant: 150 Hej då! Namn: Mjölk Pr: 6.5 Ant: 3000 Hej då! Namn: Pr: 0 Ant: 0 Hej då! Namn: banan Pr: 17 Ant: 250000 Hej då! Namn: Äpple Pr: 1 Ant: 7000 Hej då! Namn: Småfranska Pr: 0.5 Ant: 150 Hej då! Namn: Mjölk Pr: 3.25 Ant: 3000 Ge varans namn: banan Ge pris och antal i lager: 34 250000 194 195