Följande uppgifter handlar om pekare och hur de kan användas för att skapa pekarstrukturer, samt hur klasser kan stödja ansvaret för minneshatering.

Uppgift 1

Skriv den kod som behövs för att beskriva följande figur:
              /------------------\
              |                  |
     +-----+  |  +-------------+ |
first|  o-----\->|     +-----+ | |
     +-----+     |next |  o------/
       (*)       |     +-----+ |
                 |       (*)   |
                 |     +-----+ |
                 |data |  5  | |
                 |     +-----+ |
                 |      (int)  |
                 +-------------+
                    (Element)
I figuren pekar "first" på elementet, och elementets "next" pekar på elementet självt.

Uppgift 2

Givet figuren i uppgift (1) körs följande kod:

(*first->next).next->data = 10;
(*first->next).next->next = nullptr;

Förklara vad som händer när koden körs och hur samma sak går att göra med enklare kod.

Uppgift 3

Skriv den kod som behövs för att figuren i uppgift (1) skall bli som följer.
     +-----+     +-------------+      +-------------+
first|  o------->|     +-----+ |   /->|     +-----+ |
     +-----+     |next |  o-------/   |next |  0  | |
       (*)       |     +-----+ |      |     +-----+ |
                 |       (*)   |      |       (*)   |
                 |     +-----+ |      |     +-----+ |
                 |data |  5  | |      |data |  9  | |
                 |     +-----+ |      |     +-----+ |
                 |      (int)  |      |      (int)  |
                 +-------------+      +-------------+
                    (Element)            (Element)
I figuren pekar "first" på elementet som har "data"=5 och det elementets "next" pekar i sin tur på elementet med "data"=9 som i sin tur har "next"=nullptr. Antag att Element har en konstruktor Element(Element* next, int data).

Uppgift 4

Skriv den kod som behövs för att figuren i uppgift (3) skall bli som följer.
                                 /--------------------\
     +-----+     +-------------+ |    +-------------+ |  +-------------+
first|  o------->|     +-----+ | | /->|     +-----+ | \->|     +-----+ |
     +-----+     |next |  o------/ |  |next |  0  | |    |next |  o------\
       (*)       |     +-----+ |   |  |     +-----+ |    |     +-----+ | |
                 |       (*)   |   |  |       (*)   |    |       (*)   | |
                 |     +-----+ |   |  |     +-----+ |    |     +-----+ | |
                 |data |  5  | |   |  |data |  9  | |    |data |  8  | | |
                 |     +-----+ |   |  |     +-----+ |    |     +-----+ | |
                 |      (int)  |   |  |      (int)  |    |      (int)  | |
                 +-------------+   |  +-------------+    +-------------+ |
                    (Element)      |     (Element)          (Element)    |
                                   \-------------------------------------/
Elementet längst till höger är det "nya" jämfört med föregående figur. Det tidigare elementen ligger alltså kvar på sin tidigare plats i minnet. I figuren pekar "first" på elementet som har "data"=5 och det elementets "next" pekar i sin tur på nya elementet med "data"=8 som i sin tur har "next"=9 och till slut "next"=nullptr. Antag att Element har en konstruktor Element(Element* next, int data). Förenklat ritar vi ovan figur som följer:
      +-----+    +---+---+    +---+---+    +---+---+    
first |  o------>| 5 | o----->| 8 | o----->| 9 | 0 |
      +-----+    +---+---+    +---+---+    +---+---+    
        (*)      (Element)    (Element)    (Element)
I den förenklade figuren har vi även lagt elementen i den ordning som gör att figuren framstår som enkel (hur elementen egentligen ligger i minnet har vi abstraherat bort). ( Off-topic: Om du tycker figurerna verkar jobbiga att rita i emacs skall du först prova "rektangel"-kommandona i emacs, de låter dig klippa och klistra valfria rektanglar. )

Uppgift 5

Utgå från den förenklade figuren i uppgift (4) men antag att antalet element ("lådor") är okänt. Skriv en while-loop som letar upp sista elementet (det med next == nullptr). Efter while-loopen skall adressen till sista elementet finnas i en pekarvariabel.

Uppgift 6

Utgå från den förenklade figuren i uppgift (4) men antag att antalet element ("lådor") är okänt. Skriv en rekursiv funktion som letar upp sista elementet (det med next == nullptr). Funktionen skall returnera adressen till sista elementet som en pekarvariabel.

Uppgift 7

Utgå från den förenklade figuren i uppgift (4) och följande funktion:

void insert(Element* e, int i)
{
  e = new Element(e->next, i);
}

Förklara vad som händer när följande kod kör och hur det ser ut efteråt:
insert(first, 2);
I denna uppgift gäller det att noga rita upp vad som händer för att få fram rätt (eller fel beroende på hur man ser det) slutresultat.

Uppgift 8

Rita en figur som visar resultatet av följande kod:

void insert(Element* e, int i)
int* data[3];
for (int i = 0; i < 3; ++i)
{
  data[i] = new int(i);
}

Tips: Deklarationer med pekare läses bäst baklänges. T.ex. kan "int* data[3]" läsas ut som "skapa ett fält med storlek 3 som heter 'data' och lagrar pekare till heltal".

Uppgift 9

Antag att ett Element i uppgift 4 har en destruktor:

void insert(Element* e, int i)
Element::~Element()
{
   delete next;
}

Visa steg för steg vad som händer när satsen "delete first" körs på strukturen i uppgift 4?

Uppgift 10

Klasser har speciella funktioner som aktiveras "automatiskt" i vissa lägen. En programmerare har använd dessa för att bygga en enkel pekarklass för heltal. Klassen håller reda på en pekare till heltal som tas emot via konstruktor. Det finns:

class Int_Pointer
{
public:
   Int_Pointer(int* ip = nullptr) : p{ip} {}
   Int_Pointer(Int_Pointer& rhs) : p{rhs.p} { rhs.p = nullptr; }
   ~Int_Pointer() { delete p; }
   bool is_valid() { return p != nullptr; }
   int& content_of() { retrun *p; }
   
private:
   int* p;
};

Redovisa hur följande kod skulle fungera:

Int_Pointer p{ new int };
Int_Pointer q{ p };

q.content_of() = 5;
if ( p.is_valid() )
{
  p.content_of() = 7;
}

Uppgift 11

Utgå från klassen i uppgift 10. Redovisa hur följande kod skulle fungera:

Int_Pointer p{ new int };
Int_Pointer p{ new int };

if ( p.is_valid() )
{
  Int_Pointer q{ new int };
  p = q;
}

if ( p.is_valid() )
{
  p.content_of() = 7;
}

Tips: Det går inte så speciellt bra.

Uppgift 12

Utgå från klassen i uppgift 10, men anta att pekaren pekar på 1GB data (datatyp GB_Data) istället för ett heltal. Redovisa hur följande kod skulle fungera:

GB_Pointer do_some_work(GB_Pointer data)
{
  // do some work
  return data;
}

GB_Pointer untreated_data{ new GB_Data; }
GB_Pointer treated_data{ do_some_work( untreated_data ) };

Finns någon fördel med detta jämfört med följande kod?

GB_Data do_some_work(GB_Data data)
{
  // do some work
  return data;
}
GB_Data untreated_data{}
GB_Data treated_data{ do_some_work( untreated_data ) };