Sid 1 av 8 Datavetenskap Tentamen för DAV A02 Programutvecklingsmetodik, 5 p måndag 2006-01-16 kl. 08.15-13.15 Tentamen består av 8 sidor. Ansvariga lärare: Kerstin Andersson, Robin Staxhammar Tillåtna hjälpmedel: Carrano, F.M., Data Abstraction and Problem Solving with C++ Engelsk-svenskt/svensk-engelskt lexikon Betygsgränser: Tentamensgränser 3 = 20-26,5 poäng 4 = 27-33 poäng 5 = 33,5-40 poäng Kursgränser 3 = 30-39,5 poäng 4 = 40-49,5 poäng 5 = 50-60 poäng Ange alla antaganden! Skriv tydligt! (Går inte skriften att läsa är det likställt med ett felaktigt svar.) Börja varje uppgift på ett NYTT papper! Skriv alla dina svar på skrivpapper, skriv INTE på tentan! LYCKA TILL! 1 ADTn List (4 p) ADTn List finns tillgänglig i två implementerade representationer, som en länkad lista och som en arraybaserad lista, båda gjorda i C++ med klasser. a) En klientprogrammerare, Emelie, tänker använda en av de implementerade representationerna. Vilken ska hon välja? Motivera ditt svar. (2 p) b) En annan klientprogrammerare, Tor-Jan, vill använda objekt av båda representationerna. Men han har bara fått ett gränssnitt till listan och nu är han arg. Han ringer upp dig, du är ju den ansvarige för båda dessa implementationer. Han ställer nu frågan; "Hur ska jag kunna veta vilka operationer som gäller för varje specifik lista?". Din uppgift är nu att ge Tor-Jan ett välmotiverat svar, annars kommer han aldrig mer att köpa mjukvara av dig. (2 p) Sid 2 av 8 2 ADTn Bankomat (10 p) Nedan har delar av ADTn Bankomat, som skall vara en mjukvaruimplementation av en vanlig bankomat, representerats med en klassdefinition i C++. Bankomaten är en vanlig, enkel bankomat. Man kan ta ut pengar samt kontrollera sitt saldo I övrigt kan en användare trycka fel och denne måste då få chansen att rätta till sitt misstag. Av någon anledning kanske en användare vill avbryta uttaget, det måste också gå såvida hon/han inte genomfört alla de steg som krävs för att ta ut pengar. Användaren ska kunna välja om han/hon vill ha ett kontoutdrag eller inte. Du ska nu färdigställa definitionen av ADTn Bankomat genom att utöka klassdefinitionen nedan med funktioner. Att färdigställa definitionen innebär att du ska specificera alla nödvändiga operationer tillsammans med för- och eftervillkor. Den privata delen av klassdefinitionen är fullständig, där ska inget läggas till. Dessutom är den privata delen inte nödvändig för specifikationen av ADTn, utan endast för implementationen, och är endast där för att ge dig lite vägledning i specifikationen av ADTn. ********Du ska EJ implementera bankomaten. ******** OBS! Observera att detta är en ADT, du ska inte göra någon användarapplikation. Du ska anta att inga oväntade externa fel uppstår, t ex innebär det att uppkopplingen mot banken inte kan gå fel. Du ska inte blanda in banken för autentisering och uppdatering av bankkontot o s v. Tänk heller inte på displayen, den enda operation som behövs för att visa tillståndet på displayen är den som redan finns nedan. Fokusera på att bara modellera själva bankomatens funktionalitet. class Bankomat { public: //Pre: true //Post: En bankomat är skapad Bankomat() //Pre: true //Post: Bankomaten är destruerad ~Bankomat() //Pre: true //Post: Nuvarande tillstånd är utskrivet på displayen void visaTillstandPaDisplayen(); private: Display* terminalFonster; KnappSats* knappar; UtmatningsEnhet* penningUtmatare; UtmatningsEnhet* kontoUtdragsUtmatare; Sid 3 av 8 KortmatningsEnhet* kortInOchUtmatare: KortLasare* kortLasare; bool pinOK; KortNummer* kortNummer; bool kortOK; Bankomat(const Bankomat& original); Bankomat& operator=(const Bankomat& rhs); }; 3 Kontrakt (6 p) a) I klassen List ingår bl a operationerna isElement och getElement i det publika gränssnittet. Kontraktet för operationen getElement kan skrivas på två olika sätt, enligt nedan //Pre: true //Post: result = true om det finns ett element på positionen //"position", false annars. bool isElement(int position); Alternativ 1 //Pre: isElement(position) //Post: Elementet på positionen "position" har returnerats. int getElement(int position); Alternativ 2 //Pre: true //Post: Om positionen "position" är giltig har elementet på positionen "position" returnerats, annars har -1 returnerats. int getElement(int position); Vilket alternativ är bäst? Varför? Motivera ditt svar väl. (3 p) b) I klassen List finns också operationen isEmpty. //Pre: true //Post: result = true om listan är tom, false annars. bool isEmpty(); Är detta ett svagt kontrakt? Motivera ditt svar. (1 p) Sid 4 av 8 c) Kön nedan använder en lista i sin implementation. template <typename T> class Queue { public: //Pre: ??? //Post: The first element in the queue has been removed void dequeue(); //… private: List<T> queueList; }; Operationen dequeue ovan använder operationen remove i klassen List i sin implementation. template <typename T> void Queue::dequeue() { queueList.remove(1); } Listans operation remove har ett gränssnitt enligt nedan. //Pre: 1 <= position <= size() //Post: The element at the position "position" has been removed. void remove(int position) Hur måste förvillkoret för dequeue ovan se ut? Varför, samt vilka antaganden har gjorts? Motivera dina svar. (2 p) Sid 5 av 8 4 Cirkulär kö (5 p) Om man använder en array för att lagra en kö uppstår snart ett problem, nämligen att arrayen tar slut. Ett sätt att lösa problemet är att göra arrayen cirkulär, vilket innebär att man efter sista elementet (maximalt index) i arrayen går till element med index 0. Din uppgift är att skapa en klass som heter Circ_Ko utgående från nedanstående kod. Klassen skall ha normala köegenskaper vad gäller insättning och borttagande. Som du ser saknas returvärden, parametrar, datamedlemmar och implementation för funktionerna i klassen. Skriv allt detta och skriv också för- och eftervillkor. Du ska inte skriva några drivrutiner. Ledning: Som datamedlemmar är det lämpligt att ha en array där elementen i kön lagras och variabler som håller koll på köns storlek och köns början och slut. class Circ_Ko { public: typedef int datatyp; Beskrivning: En tom kö skapas. Pre: true Post: en tom kö har skapats Circ_Ko(); Beskrivning: En kö tas bort. Pre: true Post: kön har tagits bort ~Circ_Ko(); Beskrivning: Lägger till ett element sist i kön. Pre: ? Post: ? ? KoAdd(?); Beskrivning: Tar bort ett element först i kön. Pre: ? Post: ? ? KoRemove(?); Beskrivning: Kontrollerar om kön är tom. Pre: ? Post: ? ? KoIsEmpty(?); Beskrivning: Kontrollerar om kön är full. Pre: ? Post: ? ? KoIsFull(?); Sid 6 av 8 Beskrivning: Returnerar första elementet i kön. Pre: ? Post: ? ? KoGetFront(?); private: enum {ANT_I_ARRAY = 100}; // plats för datamedlemmar }; 5 Minneshantering (5 p) a) Vad är copy-konstruktorns uppgift? Vad kan hända om vi inte har definierat en copy-konstruktor och klassen handhar dynamisk minnesallokering? Exemplifiera. (3 p) b) Förutom copy-konstruktorn har varje klass tre andra speciella medlemsoperationer. Namnge dessa och förklara deras respektive uppgift. (2 p) 6 Arv och polymorfism (5 p) a) Ange tre skäl till varför arv bör användas. (2 p) b) Visa hur man på bästa sätt använder en array för att lagra objekt av klasserna Dog och Bird nedan. Visa hur man sätter in några objekt i arrayen. Visa också hur man kan hämta objekten och anropa operationerna på dem. (3 p) #ifndef PET_H #define PET_H class Pet { public: virtual void speak() = 0; virtual void eat() = 0; }; #endif //*************Dog.h***********// #include "Pet.h" class Dog : public Pet { public: virtual void speak(); virtual void eat(); }; //*************Bird.h***********// Sid 7 av 8 #include "Pet.h" class Bird : public Pet { public: virtual void speak(); virtual void eat(); }; //********************Dog.cpp**************// #include "Dog.h" #include <iostream> void Dog::speak() { std::cout << "Dog::speak()" << std::endl; } void Dog::eat() { std::cout << "Dog::eat()" << std::endl; } //*****************Bird.cpp*****************// #include "Bird.h" #include <iostream> void Bird::speak() { std::cout << "Bird::speak()" << std::endl; } void Bird::eat() { std::cout << "Bird::eat()" << std::endl; } 7 Binära träd och rekursion (5 p) a) Ett binärt träd skrivs ut i inorder och preorder med följande resultat: Inorder: 3, 8, 11, 7, 5, 12, 1, 6, 9, 2, 10, 4 Preorder: 6, 7, 8, 3, 11, 1, 5, 12, 2, 9, 4, 10 Rita upp hur trädet ser ut. (2 p) Sid 8 av 8 b) Skriv en rekursiv medlemsfunktion, sameTree(BST & aTree), i klassen BST (Binary Search Tree), som bestämmer om det binära sökträdet är lika med ett annat givet binärt sökträd. Med lika menas att träden ska ha lika struktur, samt att de har lika element på respektive plats. Det binära sökträdet är ett objekt av klassen BST och består av nod-objekt, som tillhör klassen TreeNode. Eftersom rotpekaren inte får vara synlig/åtkomlig via ett objekt av det binära trädet måste du skapa två funktioner, en publik (public) gränssnittsfunktion, som i sin tur anropar en rekursiv privat (private) funktion. (3 p) Nedan finns några kodfragment från de två klasserna, som du kan ha nytta av: class TreeNode { friend class BST; private: int data; TreeNode* left; TreeNode* right; }; class BST { public: //... bool sameTree(BST &aTree); // Implementera denna ! private: TreeNode* root; //... ? sameTree(?); //Implementera denna! };