DSV Sid 1(6) *:85/2I4123 Jozef Swiatycki 2006-01-21 Tentamen *:85/2I4123 C++ 2006-01-21 Denna tentamen består av fyra uppgifter som tillsammans kan ge maximalt 30 poäng. För godkänt resultat krävs minst 18 poäng. Väl godkänt ges för speciellt bra lösningar. För KTH-studenter krävs minst 18 poäng för betyget 3, minst 22 poäng för betyget 4 och minst 26 poäng för betyget 5. Observera: - skriv tydligt, oläsbar lösning medför 0 poäng - börja varje uppgift på nytt blad - skriv endast på ena sidan av pappret - skriv namn på varje inlämnat blad - redovisa eventuella antaganden (som givetvis inte får strida mot uppgiftstexten) - tänk på att det kan passa dig bättre att lösa uppgifterna i annan ordning Tillåtna hjälpmedel: - medhavda godtyckliga läroböcker i C och C++ - medhavda kurskompendiehäften med gult omslag (volym I, II, II och IV samt register, låneexemplar finns hos tentavakterna) eller med blått omslag (volym I, II, IV och V samt register). LYCKA TILL!
DSV Sid 2 *:85/2I4123 Uppgift 1 (5 poäng) Denna uppgift består av tre deluppgifter med var sin klass. Det är meningen att samtliga tre klasser skall ha värdesemantik. Din uppgift är att avgöra om det behövs en copy-konstruktor, en destruktor resp. en tilldelningsoperator för dessa klasser och i förekommande fall implementera dem. Om ingen copy-konstruktor, destruktor eller tilldelningsoperator behövs räcker det med att ange detta. a) Klassmallen Vector<> skall fungera som en dynamisk vektor av godtyckliga element (som dock måste ha en default-konstruktor), vektorn skall som sagt ha värdesemantik: template <class T> class Vector{ int siz; T *data; Vector(int n):siz(n), data(new T[siz]){ T& operator[](int index) { return data[index];... ; // Vector b) Klassen Lexicon tänks fungera som ett slags ordbok med uppslagsord och deras definitioner. Dessa lagras internt i standardbibliotekets map<>, Lexicon omsluter bara denna map: class Lexicon{ std::map<std::string, std::string> data; void define(std::string keyword, std::string def){ data[keyword]=def; std::string lookup(std::string keyword){ return data[keyword];... ; // Lexicon c) Klassen Managed innehåller bara en sträng, den håller dock även reda på alla sina objekt i varje ögonblick i en statisk lista av pekare till objekten: class Managed{ static std::list<managed *> objects; std::string data; Managed(std::string d): data(d){ objects.push_back(this); std::string get_data() const { return data;... ; // Managed std::list<managed *> Managed::objects; // Definition
DSV Sid 3 *:85/2I4123 Uppgift 2 (10 poäng) Denna uppgift går ut på att skapa (delar av) en klassmall för en reverse_array_iterator, nedan kallad Rai<>, samt mallar för två fria fabriksfunktioner rbegin() och rend() för skapande av Rai<>-objekt. Förklaring och motivering: standardbibliotekets behållarklasser såsom vector<>, list<>, deque<> o.s.v. har ju typen reverese_iterator, vars objekt beter sig som pekare till element i behållaren (kan avrefereras, flyttas fram eller bak o.s.v.) med den skillnaden att "plus"-operationer flyttar iteratorn tillbaka medan "minus"-operationer flyttar iteratorn framåt. Detta möjliggör användning av generiska algoritmer för baklängesgenomgångar, t.ex. givet funktionen void skriv(int x) { std::cout << x << " "; och vektorn std::vector<int> vi; kan man få den utskriven baklänges genom std::for_each(vi.rbegin(), vi.rend(), skriv); Generiska algoritmer kan även användas på vanliga inbyggda arrayer varvid pekare till arrayelementen fungerar som iteratorer, t.ex. givet arrayen int arr[] = {10,11,16,20,23,43,53,73,76; kan vi få den utskriven (i vanlig ordning) genom: std::for_each(arr, arr+9, skriv); Men det finns ju ingen motsvarighet till reverse_iterator för arrayer! Uppgift: skriv mallen för en klass Rai<> som beter sig som en reverse_iterator för arrayer, samt mallar för funktionerna rbegin() och rend(). Mallarna ska ha en typparameter, nämligen typen för arrayelementen. Med hjälp av dessa ska en tillämpning kunna få en array behandlad i baklängesordning av de generiska algoritmerna, t.ex. std::for_each(rbegin(arr,9), rend(arr,9), skriv); Tilläggskrav: de operationer på Rai<>-objekt som inte förändrar objektet ska kunna utföras både på const- och på icke-const-objekt. De medlemsfunktioner i Rai<> som består av fler än en sats ska vara definierade utanför klassdefinitionen. Konstruktion: man ska kunna definiera oinitierade Rai<>-objekt, t.ex. Rai<int> ri; eller initierade från ett annat Rai<>-objekt, t.ex. Rai<int> ri2 = ri; Man ska även kunna få fram ett Rai<>-objekt "pekande" på sista arrayelementet genom anrop till den fria funktionen rbegin(), t.ex. ri = rbegin(arr,9); Likaså ska man kunna få ett Rai<>-objekt "pekande" till elementet före första genom anrop till den fria funktionen rend(), t.ex. ri = rend(arr,9); Båda funktionerna ska ta en pekare till arrayens första element och arrayens längd (i antal element), det kan dock hända att det visar sig att något av argumenten inte behövs. Obs! dessa funktioner kommer att behöva konstruera Rai<>-objekt initierade på annat sätt än vad som angavs ovan, men det ska inte vara synligt för tillämpningar! Tilldelning, kopiering m.m.: Rai<>-objekt ska kunna tilldelas till varandra, skickas till funktioner och returneras från funktioner, men de automatiskt genererade funktioner för detta duger gott för Rai<>, så inget speciellt behöver göras för det. Uppgiften fortsätter på nästa sida!
DSV Sid 4 *:85/2I4123 Avreferering: Rai<>-objekt ska ju bete sig som pekare, så de måste implementera avrefereringsoperatorn operator* och medlemsåtkomstoperatorn operator->. T.ex. ska man kunna göra int x = *ri; eller, om man har en klass Person med medlemsfunktionen get_name() som returnerar en sträng, arrayen Person folk[10]; och Rai<Person> rp = rbegin(folk,10); så ska man kunna göra std::string namn = rp->get_namn(); "Plus"-operationer: man ska kunna flytta ett Rai<>-objekt till ett tidigare element i arrayen genom "plus"-operationer, t.ex. ri += 3; (ri flyttas 3 steg bakåt) ri2 = ri + 3; (ri förblir oförändrat men ri2 pekar på elementet 3 steg före ri) ri2 = 3 + ri; (samma effekt som den förra operationen, annan ordning på operanderna), samt med prefix resp. postfix inkrementering ++ri eller ri++ - dessa operationer flyttar ri tillbaka ett steg. På motsvarande sätt borde det finnas "minus"-operationer, men de ingår inte i uppgiften. Jämförelseoperationer: man ska kunna jämföra Rai<>-objekt med operatorerna == och!= (med betydelsen "pekar på samma arrayelemt" resp. "pekar inte på samma arrayelement"). För att få full funktionalitet borde några fler operationer implementeras och Rai<>-mallen borde förberedas så att de generiska algoritmerna ska känna igen den som en iterator, men allt detta ingår inte i uppgiften. Uppgift 3 (5 poäng) Antag följande deklarationer: class Person{ std::string fornamn, efternamn; double lon; Person(std::string f, std::string e, double lo) :fornamn(f), efternamn(e), lon(lo){ bool is_my_name(std::string f, std::string e) const { return f == fornamn && e == efternamn; void increase(double inc){ lon += inc; ; typedef std::vector<person *> Personvektor; Obs! I följande två uppgifter skall du använda tillämpliga funktionsobjektsadaptorer (bind1st, bind2nd, mem_fun) från biblioteket <functional> om det går, annars skall du skapa egna funktionsobjekt användning av funktioner istället för funktionsobjekt accepteras i nödfall men ger poängavdrag. a) Skriv en funktion Person *findpers(personvektor& folk, string f, string e); som tar en referens till en vektor av pekare till Person-objekt och två strängar och använder den generiska algoritmen find_if för att hitta och returnera pekare till ett objekt med förnamnet f och efternamnet e. Om inget sådant objekt påträffats skall pekarvärdet 0 returneras. b) Skriv en funktion void generelltpaslag(personvektor& folk, double paslag); som tar en referens till en vektor av pekare till Person-objekt och använder den generiska algoritmen for_each för att öka alla personers löner med beloppet paslag.
DSV Sid 5 *:85/2I4123 Uppgift 4 (10 poäng) Denna uppgift går ut att skriva en liten klasshierarki representerande Varelser som finns i skogen och samlar på skogens Grejer. Klasser som representerar skogens Grejer är färdiga och finns återgivna på nästa sida: Kottar och Bär som har vikt och ett näringsvärde som beräknas ur vikten och mer specifika egenskaper. Kottar kan vara mogna eller ej, Bär har en färg - vissa färger ger ett speciellt näringsvärde. Din uppgift är att skriva en klasshierarki representerande skogens Varelser, som samlar på Grejer. Se till att värdesemantik blir förbjuden för objekt av dessa klasser och att objekten endast kan skapas dynamiskt och endast kan hanteras via pekare. Alla medlemsvariabler ska vara privata. Om du vill kan du definiera icke-statiska medlemsfunktioner inuti klassdefinitionerna i denna uppgift. Se till att inte i onödan upprepa kod utan försök utforma klasserna så att det mesta av koden bara skrivs på ett ställe. Varje Varelse har ett namn och en säck (en samling av Grejer, representerad lämpligast genom någon behållarklass ur standardbiblioteket). En Varelse har även en maximal vikt den orkar bära. Varelser ska även ha en medlemsfunktion samla(), som ska ha som argument en samling med blandade Grejer. I funktionen samla() går Varelsen igenom argumentsamlingen och tittar på Grejerna där, en Grej i taget. Om Varelsen gillar Grejen (se nedan) och orkar bära den (Grejens vikt plus den totala vikten av Grejerna i säcken är inte större än vad Varelsen orkar bära) så läggs Grejen till säcken. Om säcken skulle bli för tung så kollar Varelsen i säcken efter en Grej att kasta - denna Grej måste väga minst lika mycket som den nya Grejen och ge mindre näring. Om Varelsen hittar en sådan Grej i säcken så ersätts den i säcken med den nya Grejen, annars görs ingen förändring. De Grejer som en Varelse plockar upp ska tas bort från argumentsamlingen. Varelser kan vara av olika slag, det som skiljer de åt är vilka Grejer de gillar: Sork - tar alla grejer med näringsvärde över 17, orkar bära högst 130 Ekorre - tar endast kottar med näringsvärde över 13, orkar bära upp till 300 Skata - tar endast mogna kottar och röda bär, orkar bära upp till 200 Koden för Grej-hierarkin finns på nästa sida.
DSV Sid 6 *:85/2I4123 Här är koden för Grej-hierarkin: class Grej{ int vikt; protected: Grej(int v):vikt(v){ int get_vikt() const{ return vikt; virtual int get_naring() const = 0; virtual ~Grej(){ ; class Kotte : public Grej{ bool mogen; Kotte(int v, bool m):grej(v), mogen(m){ bool is_mogen() const{ return mogen; int get_naring() const{ return get_vikt() * ((mogen)?6:3); ; class Bar : public Grej{ std::string farg; Bar(int v, std::string f):grej(v), farg(f){ std::string get_farg() const{ return farg; int get_naring() const{ return (farg == "Röd" farg == "Blå")? 23 : get_vikt(); ;