Presentation laddar. Vänta.

Presentation laddar. Vänta.

TDP004 Objektorienterad Programmering Fö 6 Objektorientering forts.

Liknande presentationer


En presentation över ämnet: "TDP004 Objektorienterad Programmering Fö 6 Objektorientering forts."— Presentationens avskrift:

1 TDP004 Objektorienterad Programmering Fö 6 Objektorientering forts.

2 Introduktion Laboration på objektorientering.
Viktiga objektorienterade begrepp: Superklass, subklass, arv. Abstrakta klasser. Virtuella funktioner. Polymorfi. Static. Laboration på objektorientering. Static är ett C++ nyckelord, inte ett generellt viktigt OO-begrepp.

3 Repetition från fö 2 I filen ExampleClass.h: class ExampleClass {
public: // Publik åtkomstdeklaration ExampleClass(int defaultID); // Konstruktor ~ExampleClass(); // Destruktor bool SetID(int aID); // Medlemsfunktioner. const int GetID() const; float f(float a, float b, float c) const; private: // Privat åtkomstdeklaration int m_ID; // Klassvariabel. }; // Observera ; För det mesta definieras funktionerna i .cc/.cpp-filen. Detta är ett exempel på en klass. Klassdeklaration med class klassnamn. Åtkomstdeklarationerna public och private har vi gått igenom tidigare. Vi ser konstruktorn och destruktorn som publika funktioner, samt 3 andra andra publika funktioner SetID, GetID och f. Det finns även en privat medlemsvariabel m_ID som är en int.

4 Repetition från fö 2, forts.
Med åtkomstdeklarationerna public, protected och private reglerar man åtkomst till klassens data och funktioner för andra klasser. Den egna klassen har alltid obegränsad tillgång. Public – fritt tillgänglig för läs och skriv. Private – ingen tillgänglighet alls. Protected – tillgänglig enbart för subklasser. I class är allt private som default. I struct är allt public som default.

5 Arv Inom OO är arv ett viktigt begrepp: vi kan se arv som specialisering/förfining av en klass och dess funktioner. Klassen i toppen av arvshierarkin kallas superklass/basklass. Den klass som ärver av superklassen kallas subklass. Arvshierarkier ritas tex med UML. Nu kommer vi till arv vilket är ett av de allra viktigaste begreppen inom OO. Här har vi lite grundläggande terminologi. Arv handlar om att det finns en arvshierarki med en superklass överst, som sedan subklasserna ärver funktioner och medlemsvariabler från. Tanken är bland annat är funktioner och variabler som är gemensamma mellan olika klasser flyttas upp till en gemensam superklass. Detta ger också fördelen att man enbart behöver programmera funktionen en gång, och undviker därmed duplicering av kod vilket är en potentiell felkälla.

6 Arv, forts. En subklass är en specialisering av en superklass.
Åtkomsten av en superklass funktioner och data varierar beroende på vilken sorts arv det är. I C++ finns det public, protected och private arv. Data/funktioner som är private i basklassen ärvs aldrig. Precis som det finns olika sorters åtkomst av funktioner och variabler så finns det olika sorters arv. Det man reglerar är vilket åtkomst superklassens funktioner får i subklassen. Värt att notera att en basklass privata funktioner aldrig kan ärvas till en subklass.

7 Olika sorters arv public: de ärvda funktionerna/datan behåller den åtkomst de hade i superklassen. protected: de ärvda funktionerna/datan blir protected i subklassen. private: de ärvda funktionerna/datan blir private i subklassen. Den ärvande klassen har alltså möjlighet att styra hur andra klasser ska komma åt de ärvda funktionerna. Man inte kan göra funktioner/data mera tillgängliga genom arv. Vi ser att den ärvande klassen till viss del kan påverka hur andra klasser kan få tillgång till de ärvda funktionerna och variablerna, men de kan aldrig bli mera tillgängliga än de var i basklassen. Detta för att behålla abstraktion och inkapsling.

8 Virtuell funktion Hittills har vi alltid implementerat alla funktioner i klassen. Vi kan även deklarera virtuella funktioner, dessa har nyckelordet virtual framför returvärdet: virtual double GetSpeed(); En virtuell funktion är ett kontrakt som alla subklasser måste uppfylla. En superklass som definierar en virtuell funktion kan själv implementera den (i C++, men inte i alla andra OO språk). Nyckelordet virtual skrivs enbart i .h-filen. Nu kommer vi till ytterligare ett viktigt koncept inom OO: virtuella funktioner. En virtuell funktion kan ses som ett kontrakt som måste uppfyllas: en funktion som måste heta en viss sak, ta vissa parametrar samt ha ett visst returvärde. Kompilatorn ser till att detta uppfylls. En virtuell funktion är på sätt och vis motsatsen till det vi pratade om att man flyttar implementationen av en funktion uppåt i arvshierarkin. Implementationen av en virtuell funktion görs normalt i subklassen.

9 Virtuella funktioner, forts.
Vad är poängen med virtuella funktioner? Att kunna delegera utförandet av en viss beräkning till subklasserna och inte bry sig om hur de utför den. Detta koncept kallas polymorfism (mångformighet) och en sådan funktion kan definieras på olika sätt. Poängen med virtuella funktioner är att vi ska kunna ha ett interface som är en kontrakt att utföra något, och seadn kunna göra detta på olika sätt i olika subklasser. Vi delegerar alltså definitionen/implementationen till subklasserna och litar på att dessa uppfyller funktionen korrekt.

10 Rent virtuell funktion
En rent virtuell funktion får inte definieras i den deklarerande klassen: virtual double GetSpeed() = 0; OBS! = 0 skrivs enbart i .h-filen. Detta gör det omöjligt att skapa en instans av klassen: det blir en abstrakt klass. Alla rent virtuella funktioner måste implementeras i subklasserna. En rent virtuell funktion kunde i C++ implementeras i basklassen, men det går även att göra funktioner som inte får implementeras i basklassen. Dessa kallas rent virtuella funktioner och markeras med = 0 efter namnet i .h-filen. Eftersom det är inte är tillåtet att implementera funktionen i basklassen blir det omöjligt att skapa en instans av den klassen: detta koncept kallas för en abstrakt klass. Likhet med interface i Java. Alla rent virtuella funktioner måste implementeras i alla subklasser, vilket kompilatorn ser till. Vi kan ta ett exempel med olika former som implementerar funktionen CalculateArea() på olika sätt.

11 Konstruktorer et.c. utelämnade.
Shape virtual double Calculate Area() = 0; Abstrakt basklass Rectangle double CalculateArea() { return height * width; } Triangle double CalculateArea() { return (height * width) / 2.0; } Det finns en abstrakt basklass Shape som har en funktion som heter CalculateArea. Det finns även två subklasser, Rectange och Triangle. Dessa implementerar funktionern CalculateArea på olika sätt. Vad en abstrakt klass är ska vi strax gå igenom. Enkelt klassdiagram. Subklasserna definierar funktionen CalculateArea() på olika sätt. Konstruktorer et.c. utelämnade.

12 Polymorfism std::vector<Shape*> shapesVector;
Shape* shape1 = new Triangle(…); Shape* shape2 = new Rectangle(…); shapesVector.push_back(shape1); shapesVector.push_back(shape2); shapesVector.push_back(…); Ex 1: for(unsigned int i = 0; i < shapesVector.size() ; ++i) { std::cout<< shapesVector[i]->CalculateArea() << ” ”; /* Anropar korrekt CalculateArea(), vi vet inte vilken implementation vid kompileringen. */ } Ex 2: shape1->CalculateArea(); //Anropar CalculateArea() i klassen Triangle. shape2->CalculateArea(); //Anropar CalculateArea() i klassen Rectangle. Fördelen med detta är att vi kan skapa olika Shapes och sedan anropa CalculateArea på dessa Shapes utan att i förväg veta vilken funktion som kommer att exekveras. I exemplet så gör vi just detta och sparar dem i en vector med Shape som element. Sedan itererar vi igenom denna vector och anropar CalculateArea för alla Shapes i vectorn. Korrekt implementation av CalculateArea kommer att anropas, men vi vet inte vilken implementation det är eftersom det kan finnas olika Shape i vectorn.

13 Polymorfism, forts. Vilken funktion som exekveras vid anrop till CalculateArea() avgörs vid exekveringen, sk dynamisk/sen bindning. En icke-virtuell funktion har sk statisk/tidig bindning: det bestäms vid kompilering vilken funktion som kommer att anropas.

14 Multipelt arv Det är fullt möjligt att ärva från flera klasser.
Deklaration som vanligt. Man får se upp om flera basklasser i sin tur är subklasser till samma basklass, och implementerar virtuella funktioner olika. Detta problem löses på olika sätt i olika OO-språk.

15 virtual int f(int a, int b) = 0;
C int f(int a, int b) { return (a * b); } B int f(int a, int b) { return (a + b) / 2; } D D* d = new D(); d->f(1, 3);? Vad händer då funktionen f anropas i klassen D? Detta utseende i arvshierarkin kallas för ”The diamond pattern”.

16 Virtuella destruktorer
En virtuell destruktor i basklassen behövs vid arv, för att säkerställa att allt minne i basklassen lämnas tillbaka. En basklass bör definiera en virtuell destruktor även om basklassen inte har något att ta bort. En kompilatorgenererad destruktor är aldrig virtuell. Det är ingen nackdel att göra alla destruktorer virtuella.

17 Static En static medlems-/variabel-funktion finns bara i ett exemplar som är gemensam för klassen, den hör alltså inte till någon instans av klassen. Användbart när man vill ha någonting som det bara finns en av och som alla instanser kan använda. Ex på nästa slide: alla instanser av en klass ska ha ett unikt ID-nummer. C++ nyckelord static talar om att något, det vi deklarerar static, bara existerar i ett exemplar. Detta är gemensamt mellan alla instanser och kan användas av alla.

18 Static variabel I .h-filen I .cc-filen
static int ourID; // Static variabel. int m_ID; // Vanlig medlemsvariabel. I .cc-filen /* En static variabel måste initieras utanför funktionerna. */ int classname::ourID = 1; // Men kan användas i alla klassens funktioner. classname:: classname() { m_ID = ourID; // Tilldela ett unikt ID. ourID++; }

19 Lokala static variabler
Även lokala varibler kan vara static. Ex: Vi vill igen ge alla instanser ett unikt ID-nummer. classname:: classname() { static int ourID = 0; // Exekveras endast en gång. m_ID = ourID; // Tilldela ett unikt ID. ourID++; } Nackdelen jämfört med förra metoden är att vi inte har tillgång till ourID utanför konstruktorn, om vi t.ex. skulle vilja få reda på det nuvarande värdet på ourID. En lokal variabel som är static är lite speciell. Den finns också bara i ett exemplar som delas mellan de olika instanserna, och den håller sitt värde mellan anropen till funktionen, och kan alltså i en del applikationer användas för att spara ett tillstånd istället för en static medlemsvariabel.

20 Static funktioner Även funktioner kan vara static.
En static funktion kan anropas även om det inte finns några instanser av klassen. Får endast använda statiska klassvariabler (lokala variabler som vanligt). Kan endast anropa andra funktioner som inte tar this-pekare som parameter (static-funktioner och konstruktor). I tidigare föreläsning pratade vi om att de flesta funktioner tar en osynlig extra parameter, this. Undantagen är konstruktorer och static variabler. Detta gör dem speciella eftersom det går att anropa dem även om det inte finns någon this, dvs det går att anropa dem utan att det finns någon instans av klassen. Men det för också med sig att de enbart kan anropa andra funktioner som även de är static, samt enbart använda medlemsvariabler som är static. Orsaken till detta är att annars skulle en static variabel kunna ändra på ett objekts tillstånd, utan att det finns någon instans av objektet.

21 Copy constructor och tilldelningsoperator
Copy constructor skapar en kopia av instansen. Tilldelningsoperator tilldelar endast. Kompilatorns copy constructor är public som default. Utseende enligt: klassnamn(klassnamn&) Ex: ExampleClass(const ExampleClass&); Skapas av kompilatorn om inte programmeraren gör det.

22 Copy constructor och tilldelningsoperator, forts.
Tilldelningsoperator för att tilldela en instans. Utseende enligt: klassnamn& operator=(const klassnamn&) Ex: ExampleClass& operator=(const ExampleClass&); Defaultbeteende för den automatgenererade tilldelningsoperatorn är bitvis kopiering av data. Ett exempel på bitvis kopiering av data: If medlemsvariabeln är en pekare kopieras pekarens adress istället för pekarens innehåll.

23 Exempel där tilldelningsoperator behövs
class Athlete { public: Athlete(const char* value) ~Athlete(); private: char* name; };

24 Exempel, implementation
Athlete::Athlete(const char* value) { if(NULL != value) name = new char[strlen(value) +1]; strcpy(name, value); } else name = new char[1]; name= ’\0’; Athlete::~Athlete() delete [] name;

25 Exempel, användning Athlete* A = new Athlete(”Sanna Kallur”);
Athlete* B = new Athlete(”Henrik Larsson”); Henrik Larsson\0 Sanna Kallur\0 B A name name

26 Exempel, tilldelning B=A; Vad hände? Henrik Larsson\0 Sanna Kallur\0 A
name Eftersom det inte finns någon tilldelningsoperator, skapar C++ en som kopierar datan bitvis. name

27 Exempel, forts. Problem Lösning
Minnesläcka. Ursprungligt minne för variabeln ”name” för instans B (”Henrik Larsson\0”) kommer inte att tas bort. Antag att destruktorn för instans A körs och name tas bort. Vad händer när konstruktorn för instans B körs? Lösning Implementera tilldelningsoperator vilken korrekt kopierar name.

28 Exempel där copy constructor behövs
Antag att ingen copy constructor finns. void doNothing(Athlete a) { //Nothing is performed. } Athlete C(”Mats Sundin”); doNothing(C); Vad händer med C när doNothing returnerar? doNothing tar ju hela objektet, ingen pekare eller referens. Destruktorn körs när doNothing returnerar. Generellt kan man säga att när man allokerar minne i konstruktorn behöver man cc och = En nödlösning är att deklarera cc/= private men implementera dem inte. Kompilatorn kan inte skapa egna och de kan inte användas av något annat än klassen själv.

29 Sammanfattning Arv, superklass och subklass.
Public, protected och private arv. Multipelt arv. (Rent) virtuella funktioner, abstrakta klasser. Polymorfi. Dynamisk och statisk bindning. Nyckelordet static för funktioner och variabler.


Ladda ner ppt "TDP004 Objektorienterad Programmering Fö 6 Objektorientering forts."

Liknande presentationer


Google-annonser