DD2387 Programsystemkonstruktion med C++ Tentamen 1 Torsdag 7 januari 2016, 14:00-18:00 Introduktion Skriv dina svar på separata papper, dessa scannas in efter inlämning. Du kan skriva på både fram- och baksida. Skriv namn och uppgiftsnummer på varje sida som lämnas in för rättning Det går att lämna blad från detta häfte med svar på men bladet måste numreras, signeras och häftstiftet tas bort. Grova fel ger underkänt, men gör ett försök på alla uppgifter När det står rita så förväntas en illustration (oftast är det minnet som ska illustreras). Det är ingen självrättning efter tentan. Hjälpmedel En, eller två, valfria läroböcker om C++. 1 of 8
1 Explicita regler Det är mycket prat om flyktingar som söker asyl i diverse länder. Kanske beror en del av pratet på att asylrätten, rätten att söka asyl, inte explicit står med i den flyktingkonvention som konstruerades 1951 och som det ofta refereras till i debatten. Vad som gäller för rätten att söka asyl varierar bland de 146 länder som skrivit under konventionen. Konstanta och varaktiga lösningar synes sällsynta. I C++ är det viktigt att hålla reda på vilka regler som gäller för deafultkonstruktorer, copy-konstruktor och explicita konstruktorer. Dessutom måste man ha koll på referenser och vad som varierar och vad som är konstant. Studera koden nedan: 01 struct A { 02 explicit A(int x); 03 void foo(const A & a) const; 04 05 bool operator<(a & a) { 06 this -> m_x = a.m_x - 1; 07 return true; 08 09 int m_x; 10 ; 11 12 struct B { 13 explicit B(); 14 B(const B &) = delete; // ingen copy-constructor 15 void bar(a a); 16 void what(a & a) const; 17 ; 18 19 A globala(0); // global variabel; 20 B globalb; // global variabel; 21 22 int main() { 23 A a1; 24 A a2(2); 25 B b = globalb; 26 B b2; 27 28 const A & aref = globala; 29 const B & bref = globalb; 30 31 A a4 = aref; 32 33 aref.foo(a2); 34 bref.bar(aref); 35 bref.what(aref); 36 2 of 8
Frågor a) Vad menas med att en konstruktor är explicit? Ge exempel på motsatsen, en implicit konstruktion av ett objekt. Att objektet endast kan konstrueras med ett explicit konstruktoranrop. Ett exempel på implicit konstruktion är i labb1 Vektor v = 2 som implicit anropar konstruktorn som tar ett heltal som argument. Just konstruktorer som tar ett argument bör man överväga om de ska vara implicita eller inte. b) När skapas en defaultkonstruktor automatiskt? När ingen annan konstruktor är definierad c) När skapas en kopieringskonstruktor automatiskt? I princip alltid om man inte själv definierar den eller delete-definierar den d) Koden ovanför main kompilerar utan problem men en eller flera rader i main kompilerar inte. Gå igenom de 10 kodraderna och avgör om de kompilerar eller inte. Skriv också en kort förklaring till varje kompileringsfel. 22 int main() { 23 A a1; // FEL - det genereras ingen defaultkonstruktor 24 A a2(2); 25 B b = globalb; // FEL - kopieringskonstruktorn finns inte (= delete 26 B b2; 27 28 const A & aref = globala; 29 const B & bref = globalb; 30 31 A a4 = aref; 32 33 aref.foo(a2); 34 bref.bar(aref); // FEL - bref är const men bar-metoden är icke-const 35 bref.what(aref); // FEL - aref är const men parametern a är icke-con 36 e) Att en jämförelsefunktion, såsom i A, ändrar på operandernas värden är märkligt. Skriv om deklarationen av operator< så att sådana fel fångas vid kompilering. Båda operandernas värden kan ändras och det borde de rimligtvis inte kunna göras. Det be två const. 05 bool operator<(const A & a) const { 3 of 8
2 Samordnade regler Alla länders olika regler kanske borde samordnas. Det finns en samordningsalgoritm som kallas mergesort i datalogin. Frågor a) Skriv en typparametriserad funktionsmall (template) som heter mergeprint vilken givet två sorterade mängder (definierat av fyra iteratorer se exempel), skriver ut alla element i sorterad ordning. template <class T> void mergeprint(t i1, T i2, T e1, T e2) { while (i1!= i2 && e1!= e2) { if (*i1 < *e1) { cout << *i1 << " "; ++i1; else { cout << *e1 << " "; ++e1; //cout << " "; while (i1!= i2) { cout << *i1 << " "; ++i1; //cout << " "; while (e1!= e2) { cout << *e1 << " "; ++e1; cout << endl; //... std::vector<int> v1 = {11, 22, 23, 34, 45; std::vector<int> v2 = {3, 14, 15, 36, 27, 66, 4; std::sort(v1.begin(), v1.end()); std::sort(v2.begin(), v2.end()); mergeprint(v1.begin(), v1.end(), v2.begin(), v2.end()); // skriver ut: 3 4 11 14 15 22 23 27 34 36 45 66 b) Vad ställer din kod för krav på paramaterarna (iteratorerna)? Att de kan adderas ett steg i taget med ++ och att de kan jämföras med!= 4 of 8
c) Vad ställer din kod för krav på mängdinnehållet (det iteratorerna pekar på)? Att de kan jämföras med < och avrefereras med * d) Modifiera (om det behövs) din kod så att koden fungerar för olika typer av mängder. Det behövs en till typparameter S men annars så fungerar koden eftersom man kan jämföra decimaltal och heltal med < std::vector<float> vf1 = {11.01, 22.01, 23.01, 34.01, 45.01; std::vector<int> v1 = {11, 22, 23, 34, 45; mergeprint(v1.begin(), v1.end(), vf1.begin(), vf1.end()); e) Fungerar din kod även för listor? Vad bör man tänka på? Det man bör tänka på är kraven på iteratorn. En List::iterator kan inte adderas med += eftersom den inte är random-access. std::list<int> l1 = {3, 14, 15, 36, 27, 66, 04; std::vector<int> v1 = {11, 22, 23, 34, 45; mergeprint(v1.begin(), v1.end(), l1.begin(), l1.end()); 5 of 8
3 IDkort bland molnen Pendelresenärer Köpenhamn-Malmö måste numera få sina id-handlingar kontrollerade. För att säkerställa att id-kontrollen gjorts tar man ett mobilfoto på id-handlingen som därefter lagras på någon moln-server någonstans. Bolagen hävdar att alla uppgifter är säkra och förstörs efter en viss tid. Att destruera objekt är viktigt även i C++. I koden nedan finns en pekare till en vektor som innehåller foton. Alla pekare förväntas peka på dynamiskt allokerade objekt. class Idkort { //... public: void operator()(char x) { m_stamps += x; std::string m_stamps; ; class Fotoalbum { std::vector<idkort*> * massa_foton; public: ~Fotoalbum(); //... Frågor a) Implementera en destruktor för Fotoalbum som inte läcker minne. Det finns två pekare, dels medlemspekaren och vektorns pekare. Samtliga pekar på alloker Ingendera är allokerad som array []. Idkort::~Idkort() { for (auto foto_pekare : massa_foton) { delete foto_pekare; delete massa_foton; b) Klassen Idkort är en så kallad funktor (eng: functor/function object). Skriv kod som anropar funktorn. Idkort i; i( a ); 6 of 8
4 Virtuella idkontroller I en framtida virtuell verklighet så kommer id-kontrollerna att fungera smidigt och smärtfritt. Eventuellt känsliga uppgifter kommer hanteras dynamiskt och inte läcka ut till obehöriga hackare. Minnesläckor, virtuell funktionalitet och dynamisk bindning måste man även hantera i C++. Studera nedanstående kod. #include <iostream> #include <string> struct A { explicit A (int val) : m_id (val) { ~A() { virtual void write_id() { std::cout << "base id nr: " << m_id << std::endl; ; int m_id; struct B : A { B (int x, std::string y) : A (x), m_idtype(y) { // arv virtual void write_id() { std::cout << "sub id: " << m_id << " : " << m_idtype << std::endl; std::string m_idtype; ; /////////////////////////////////// // Other member functions/variables //... void slicer(a a) { a.write_id(); B b(333, "Pass"); A * a_ptr = new B(555, "Nationellt id-kort"); // globala varibler enbart för // tentauppgiften 7 of 8
Frågor a) Rita berörda objekt och förklara varför utskriften blir som den blir om slicer anropas på följande vis. slicer(b); b -> -------------- A B m_id ----------- --------------- Objektet b pekar på skickas by value och kapas till ett A. Därför körs A:s implementatio a ----------- A m_id ----------- b) Rita berörda objekt och förklara varför utskriften blir som den blir om write_id anropas på följande vis. a_ptr -> write_id(); a_ptr pekar på ett b-objekt. write_id är en virtuell funktion och i runtime slås det upp implementation som ska köras nämligen B:s implementation. a_ptr -------------- A B m_id ----------- --------------- c) Förklara vad som är problematiskt om man felaktigt försöker frigöra minne med följande sats delete [] a_ptr; Det är odefinierat vad som händer vilket är problematiskt. d) Om man istället för ovanstående försöker frigöra minnet med nedanstående sats så är det fortfarande problematiskt. Varför? delete a_ptr; A har ingen virtuell destruktor. e) Förklara kortfattat vad dynamisk bindning är. I runtime avgörs vilken medlemsfunktion som ska köras givet en basklass-pekare/referens till en virtuell medlemsmetod 8 of 8