1 Klasser och objektorientering Vad är objektorientering? Det finns olika synsätt på programmering, dessa olika synsätt kallas för paradigm. De vanligaste paradigmen är det imperativa/proceduriella, det funktionella, det deklarativa och det objektorienterade. Det imperativa/proceduriella paradigmet har ni använt i föregående kurs, Programmeringsteknik. Det innebär att ett program ses som en sekvens av händelser/programsatser. I det funktionella paradigmet består programmet av funktioner. Mer komplicerade funktioner komponeras utifrån mindre funktioner. Ett exempel är om du har en funktion som adderar talen i en talföljd samt en funktion som räknar antalet tal i en talföljd. Dessa två funktioner kan då kombineras för att skapa en funktion som räknar ut medelvärdet av talen i en talföljd, de två funktionernas utdata blir indata till den nya funktionen. Det deklarativa paradigmet går ut på att man beskriver problemet man vill lösa på ett exakt och koncist sätt. Utifrån problembeskrivningen "ges" sedan en korrekt algoritm. Detta paradigm skiljer sig avsevärt från de andra eftersom man inte formulerar en algoritm för att lösa problemet, utan endast ger en problembeskrivning. Paradigmet grundar sig på matematisk logik och används främst för utveckling av AI-applikationer. I det objektorienterade paradigmet ses ett program som en samling av komponenter kallade objekt. Dessa objekt kan kommunicera genom att skicka meddelanden till varandra. Det objektorienterade paradigmet är väl lämpat att använda, både för att implementera ADTer (du behöver inte känna till vad ADTer är ännu) och för att konstruera mjukvara generellt, därför kommer detta paradigm att användas genomgående i kursen. Det innebär att ni kommer att få en viss träning i och introduktion till objektorientering i denna kurs även om fokus inte ligger på objektorienterad programmering i sig. 1.1 Objekt Vad kan utgöra ett objekt? Det finns en bra tumregel. Om ett ord är ett substantiv kan det representeras genom ett objekt. D v s om man kan sätta en eller ett framför ordet. Några exempel är, en person, en bil, en katt, en lista o s v. Varje objekt karaktäriseras av tre viktiga egenskaper: Identitet Operationer Tillstånd Identitet Att ett objekt har en identitet innebär att varje specifikt objekt kan identifieras. Varje objekt är alltså unikt. Jämför med t ex en person, varje person är unik och kan identifieras. Operationer Varje objekt kan associeras med ett antal operationer som bestämmer ett objekts beteende och vad det kan utföra, t ex vilka tjänster det tillhandahåller. Om vi återigen använder objektet person som ett exempel kan några operationer vara spring, gå och ät. Det är med hjälp av 1
operationerna man skickar meddelanden till ett objekt. Man anropar ett objekts operationer. Olika objekt kan anropa varandras metoder, det är så kommunikationen går till. Jämför med två olika personer, vilka kan ses som två skilda objekt, som gör varandra en tjänst. Var och en begär en tjänst av den andre genom att på något sätt skicka ett meddelande. Anmärkning: Givetvis kan objekt av olika typer också kommunicera. Exempelvis kan en person kommunicera med en hund. Operationer kallas också för metoder och i C++ även funktioner, dessa ska ni betrakta som synonymer. Ni kommer att känna igen dessa operationer/funktioner även för objekt eftersom de ser lika ut som de funktioner ni kommit i kontakt med i kursen Programmeringsteknik. Tillstånd Ett objekts beteende beror av tidigare händelser och därför kan man säga att ett objekt har ett sorts minne. Detta minne betecknas som ett tillstånd. Jämförelsen med en person är fortfarande relevant, som människa influeras man av de erfarenheter man samlat på sig. Varje erfarenhet sätter sitt spår, i ditt minne, på kroppen o s v. Ett objekt befinner sig alltid i ett visst tillstånd. Tillståndet kan varieras. Om tillståndet förändras är detta en konsekvens av att en operation som förändrar objektets tillstånd har anropats. Jämför återigen med en person, säg att vi har en person som för tillfället är glad. Plötsligt förolämpas den glada personen av en annan person. Tillståndet för den nyss glada personen kommer då att förändras från glad till arg och/eller ledsen. Det finns också operationer som inte förändrar tillståndet för ett objekt, utan har till uppgift att på något sätt inspektera tillståndet. Jämför med en person som frågar en annan person om denne är glad. Svaret som fås säger något om den tillfrågades tillstånd. 1.2 Klasser Klasser är C++ och andra objektorienterade språks sätt att definiera och implementera datatyper. Med hjälp av klasser kan man skapa objekt. Klassen utgör en mall för att skapa objekt. Jämför med en gjutform för vevstakar. För att skapa en konkret vevstake, som ju är ett objekt, använder vi gjutformen genom att hälla i flytande metall. Gjutformen kan då sägas utgöra en mall för att skapa vevstakar. Skillnaden gentemot klassen är att gjutformen till slut inte går att använda p g a att den slitits ut. En klass slits aldrig ut, det går att skapa oändligt många objekt utifrån en klass. Detta är en generell observation som gäller mjukvara, mjukvara slits inte ut på så sätt som materiella ting. Den kan bli föråldrad på så vis att man önskar ny eller förändrad funktionalitet, men själva mjukvaran fungerar precis som den alltid gjort, att det gått 20 år sedan den konstruerades har ingen betydelse för dess funktionsduglighet. Med hjälp av klasser skapar vi och implementerar sammansatta datatyper. En sammansatt datatyp kan förenklat sägas vara en datatyp som består av andra datatyper. Den sammansatta datatypen har också ett antal operationer som kan utföras på den. I föregående kurs har ni använt structen för att skapa en sammansatt datatyp, i denna kurs kommer vi endast att använda klasser. Structen stödjer inte objektorientering, och är heller ingen konstruktion som återfinns i andra objektorienterade programspråk. Då man skapar ett objekt utifrån en klass säger man att man instansierar ett objekt. Ett objekt är en instans av en klass. 2
1.3 Ett exempel på en klass skriven i C++, samt användning av denna Här följer ett exempel på hur en klass, som försöker avbilda en person, kan se ut i C++. /************Person.h***************************/ // Klassdefinition class Person public: //Denna operation anropas när objektet skapas (konstrueras). Person(char* _namn); //konstruktor //Denna operation anropas när objektet destrueras. ~Person(); //destruktor //Operationen returnerar namnet char* hamtanamn(); //Operationen förolämpar personen. void forolampa(); //Operationen ger personen en komplimang void gekomplimang(); //Operationen frågar personen om denne är glad. bool arglad(); private: //variabeln namn representerar en persons namn som är en del av //tillståndet char* namn; ; //variablen glad representerar huruvida en person är glad //eller inte och är en del av tillståndet //glad == true innebär att personen är glad och glad == false //innebär att personen inte är glad. bool glad; //end Person.h /************Person.cpp***************************/ #include Person.h #include <string> //Definition av operationerna/funktionerna //Implementationen är inte gjord, endast pseudokod har skrivits. Person::Person(char* _namn) 3
//initiera objektets tillstånd //d v s datamedlemmarna namn och glad //Låt en person alltid vara glad från början Person::~Person() //Återlämna dynamiskt allokerat minne till systemet. char* Person::hamtaNamn() // returnera personobjektets namn void Person::forolampa() // definiera vad som ska hända om någon förolämpar personen void Person::geKomplimang() // definiera vad som ska hända om någon förolämpar personen bool Person::arGlad() // returnera personobjektets lyckotillstånd // end Person.cpp /************main.cpp***************************/ //I denna fil kommer vi att nyttja klassen Person för att skapa //Person-objekt. Denna fil och dess funktioner är en klient till //klassen Person. #include <iostream> #include "Person.h" int main() //Skapa Personobjektet Janne, konstruktorn anropas Person Janne( Janne Nilsson ); //Skapa Personobjektet Anna, konstruktorn anropas Person Anna( Anna Andersson ); using std::cout; using std::endl; cout << Anna.hamtaNamn(); if(anna.arglad()) 4
cout << är glad." << endl; else cout << är ledsen." << endl; Janne.forolampa(); cout << Janne.hamtaNamn(); if(janne.arglad()) cout << är glad." << endl; else cout << är ledsen." << endl; Janne.geKomplimang(); cout << Janne.hamtaNamn(); if(janne.arglad()) cout << är glad." << endl; else cout << är ledsen." << endl; return 0; //Då vi går ur funktionen kommer destruktorn för de båda objekten att köras atomatiskt. 1.4 Angående klassen Nedanstående observationer kommer att återkopplas mot exempelklassen ovan. En klass definieras m h a nyckelordet class i C++. representerar en abstraktion. Med abstraktion avses bl a att man endast tar med de delar som är av intresse. Givetvis är en verklig person mer komplex än vad klassen ovan speglar, jag har med hjälp av klassen abstraherat det beteende (med hjälp av operationerna) och de egenskaper jag är intresserad av i detta specifika fall. Med abstraktion avses också att man tillhandahåller ett gränssnitt som gör det enkelt att skapa och använda objekten. Mer om detta nedan i sektionen om public. kan jämföras med typer i övrigt, t ex int. int x; x får alla egenskaper och operationer som typen integer (heltal) har. Vad har datatypen int för operationer? har en publik (public) och en privat (private) del. har operationer/funktioner (synonymer) som är medlemmar i klassen. Operationerna ger oss ett gränssnitt mot objektet, d v s ett sätt att kommunicera med objektet. Operationerna återfinner du under klassens public-deklaration. public-del kan klienter använda. Det som finns i public-delen kan man alltså använda utifrån. Denna del kallas för klassens/objektets gränssnitt. har egenskaper/attribut som är medlemmar i klassen.. Egenskaper och attribut används ofta som synonymer. Det är dessa som utgör objektets tillstånd. Rent tekniskt är dessa 5
datamedlemmar i klassen och du återfinner dem under private-deklarationen. Man använder således datamedlemmarna för att representera tillståndet. Varje objekt som skapas får en egen uppsättning av dessa datamedlemmar. private-del gör det möjligt att skydda datamedlemmarna, som utgör objektets tillstånd, från missbruk. Endast klassens medlemmar har tillstånd att komma åt dessa. Datamedlemmar ska alltid vara privata. inkapslar data och operationer/funktioner i klassen. Inkapsling är ett viktigt begrepp i objektorientering och innebär att man kan "samla allt på ett ställe". I klassen samlar man det som tillhör den, d v s operationer och datamedlemmar. ger en modulär design, varje klass blir en modul. Klasser delas, i C++, lämpligen upp i en h-fil och en cpp-fil. Klassdefinitionen läggs i h-filen, och definitionen/implementationen av operationerna läggs i cpp-filen. Filerna ska helst också namnges utifrån klassen enligt Person-klassen ovan. befrämjar återanvändning, man kan skapa obegränsat många objekt från samma klass. har en konstruktor som anropas då man skapar objektet. I implementationen för konstruktorn måste man initiera objektets tillstånd, d v s man måste ge datamedlemmarna vettiga värden. Det måste man göra för att ett nyskapat objekt ska befinna sig i ett vettigt tillstånd. o En konstruktor har samma namn som klassen. o Det kan finnas flera konstruktorer i en klass, de måste kunna skiljas åt av parameterlistan. o En konstruktor har ingen returtyp. har en destruktor som anropas automatiskt då objektet går ur scope ("dör"). Denna ska man aldrig anropa som klient. I implementationen för destruktorn ska man återlämna eventuellt dynamiskt allokerat minne till systemet. Minne som allokerats m h a new måste tas bort med delete. o En destruktor har samma namn som klassen. Namnet föregås av tildetecknet (~) o Det kan endast finnas en destruktor. o En destruktor har inga parametrar. o En destruktor har ingen returtyp. Objektets identitet utgörs av dess variabelnamn, i exemplet ovan har de två objekten identiteterna Janne respektive Anna. Varje objekt får sin identitet när de skapas. Den som skapar objekten namnger dem och ger dem därmed en identitet. 6