Objektorienterad programmering: Lösningar till tentamen 2006-11-01

Det här är förslag på lösningar. Det kan finnas andra lösningar som också är korrekta, och det kan hända att en del av lösningarna är mer omfattande än vad som krävs för full poäng på uppgiften. En del av lösningarna är kanske inte fullständiga, utan hänvisar bara till var man kan läsa svaret.

Uppgift 1 (2 p)

Matvarde::Matvarde(float matvarde, float felgrans) {
  this->matvarde = matvarde;
  this->felgrans = felgrans;
}

float Matvarde::get_matvarde() {
  return matvarde;
}

float Matvarde::get_min() {
  return matvarde - matvarde * felgrans / 100;
}

float Matvarde::get_max() {
  return matvarde + matvarde * felgrans / 100;
}

Uppgift 2 (2 p)

int main() {
  Matvarde m(30.0, 20.0);
  if (m.get_matvarde() == 30.0 && m.get_min() == 24.0 && m.get_max() == 36.0)
    cout << "Rätt!" << endl;
  else
    cout << "Fel!" << endl;
}

Kommentar: Man bör undvika likhetsjämförelser mellan flyttal, eftersom de inte alltid representeras exakt, men med de värden vi använder här är flyttalen små, hela tal, vilket gör att risken för avrundningsfel är liten.

Uppgift 3 (2 p)

class Datapunkt {
private:
  Matvarde matvarde;
  float tidpunkt;
  Datapunkt* next;
public:
  Datapunkt(Datapunkt* next, float matvarde, float felgrans, float tidpunkt);
  float get_matvarde();
  float get_min();
  float get_max();
  float get_tidpunkt();  
  Datapunkt* get_next();
};

Kommentar: Man kan ha en (static-deklarerad) klassvariabel som pekar ut den första datapunkten i listan, men eftersom den då blir gemensam för hela klassen, så blir det bara en enda länkad lista. Det är förmodligen dumt, om man ska ha flera sensorer (i de senare uppgifterna) som var och en har sin egen lista av datapunkter.

Uppgift 4 (3 p)

Datapunkt::Datapunkt(Datapunkt* next, float matvarde, float felgrans, float tidpunkt)
  : matvarde(matvarde, felgrans) {
  this->next = next;
  this->tidpunkt = tidpunkt;
}

float Datapunkt::get_matvarde() {
  return matvarde.get_matvarde();
}

float Datapunkt::get_min() {
  return matvarde.get_min();
}

float Datapunkt::get_max() {
  return matvarde.get_max();
}

float Datapunkt::get_tidpunkt() {
  return tidpunkt;
}

Datapunkt* Datapunkt::get_next() {
  return next;
}

Uppgift 5 (3 p)

int main() {
  Datapunkt* d1p = new Datapunkt(NULL, 3.0, 20.0, 17.0);
  Datapunkt* d2p = new Datapunkt(d1p, 4.0, 20.0, 17.1);
  Datapunkt* d3p = new Datapunkt(d2p, 5.0, 20.0, 17.2);

  Datapunkt* p = d3p;
  float minsumma = 0.0;
  float maxsumma = 0.0;
  while (p != NULL) {
    minsumma += p->get_min();
    maxsumma += p->get_max();
    p = p->get_next();
  }
  cout << "Summan av minvärdena: " << minsumma << endl;
  cout << "Summan av maxvärdena: " << maxsumma << endl;
}

En alternativ lösning (med samma Datapunkt-klass som tidigare):

int main() {
  Datapunkt d1(NULL, 3.0, 20.0, 17.0);
  Datapunkt d2(&d1, 4.0, 20.0, 17.1);
  Datapunkt d3(&d2, 5.0, 20.0, 17.2);

  Datapunkt* p = &d3;
  float minsumma = 0.0;
  float maxsumma = 0.0;
  while (p != NULL) {
    minsumma += p->get_min();
    maxsumma += p->get_max();
    p = p->get_next();
  }
  cout << "Summan av minvärdena: " << minsumma << endl;
  cout << "Summan av maxvärdena: " << maxsumma << endl;
}

Uppgift 6 (9 p)

a) (2p) Skriv specifikationen (klassdefinitionen) för klassen Sensor.

class Sensor {
private:
  string namn;
  float felgrans;
  Datapunkt* first;
public:
  Sensor(string namn, float felgrans);
  void ny_matpunkt(float matvarde);
  float get_medel();
};

b) (1p) Implementera konstruktorn.

Sensor::Sensor(string namn, float felgrans) {
  this->namn = namn;
  this->felgrans = felgrans;
  this->first = NULL;
}

c) (2p) Implementera medlemsfunktionen ny_matpunkt.

void Sensor::ny_matpunkt(float matvarde) {
  Datapunkt* dpp = new Datapunkt(first, matvarde, felgrans, get_current_time());
  first = dpp;
}

En alternativ lösning:

void Sensor::ny_matpunkt(float matvarde) {
  first = new Datapunkt(first, matvarde, felgrans, get_current_time());
}

d) (3p) Implementera medlemsfunktionen get_medel.

float Sensor::get_medel() {
  Datapunkt* p = first;
  float summa = 0.0;
  int antal = 0;
  while (p != NULL) {
    summa += p->get_matvarde();
    ++antal;
    p = p->get_next();
  }
  return summa / antal;
}

e) (1p) Skriv en main-funktion som skapar en sensor, lägger in tre mätvärden med hjälp av ny_matpunkt, och till sist skriver ut medelvärdet som fås från get_medel.

int main() {
  Sensor s1("Sensor nummer 1", 10.0);
  s1.ny_matpunkt(100);
  s1.ny_matpunkt(200.0);
  s1.ny_matpunkt(300.3);
  cout << "Medelvärdet: " << s1.get_medel() << endl;
}

Uppgift 7 (3 p)

a) (1p)

Det uppstår en minnesläcka, vilket betyder att minne reserveras (med new), men sen när minnet inte längre behövs, och inte går att nå via någon pekare, så fortsätter det att vara reserverat, och lämnas aldrig tillbaka till systemet (med delete) för att återanvändas. (Men när programmet avslutas, återlämnas normalt allt minne till operativsystemet.)

b) (2p)

Sensor::~Sensor() {
  Datapunkt* p = first;
  while (p != NULL) {
    Datapunkt* old_p = p;
    p = p->get_next();
    delete old_p;
  }
}

Uppgift 8 (3 p)

#include <fstream>
#include <cstdlib>

using namespace std;

int main() {
  ofstream tsut("testdata.txt");

  srand(time(0));
  for (int i = 0; i < 1000000; ++i) {
    tsut << 100.0 * rand() / RAND_MAX << endl;
  }
}

Uppgift 9 (4 p)

#include <iostream>
#include <fstream>

using namespace std;

#include "Matvarde.h"
#include "Datapunkt.h"
#include "Sensor.h"

int main() {
  ifstream tsin("testdata.txt");
  Sensor s1("Sensor nummer 1", 10.0);
  float talet;
  while (tsin >> talet) {
    s1.ny_matpunkt(talet);
  }
  cout << "Medelvärdet: " << s1.get_medel() << endl;
}
Kommentar: Följande main-funktion är fel, eftersom medlemsfunktionen eof, precis som funktionen feof från stdio-biblioteket, blir sann först efter den första misslyckade läsningen från filen. Se C++-FAQ 15.5 och C-FAQ 12.2. .
int main() {
  ifstream tsin("testdata.txt");
  Sensor s1("Sensor nummer 1", 10.0);
  float talet;
  while (!tsin.eof()) {
    tsin >> talet;
    s1.ny_matpunkt(talet);
  }
  cout << "Medelvärdet: " << s1.get_medel() << endl;
}

Uppgift 10 (9 p)

a) (1p)

class Sax : public VassSak {
public:
  Sax(int vasshet);
};

b) (1p)

Sax::Sax(int vasshet) : VassSak(vasshet) { }

c) (1p)

class Boll : public RundSak {
private:
  double studskoefficient;
public:
  Boll(double diameter, double studskoefficient);
  virtual void peta();
};

d) (1p)

Boll::Boll(double diameter, double studskoefficient) : RundSak(diameter) {
  this->studskoefficient = studskoefficient;
}

e) (1p)

void Boll::peta() {
  cout << "Studselistuds!" << endl;
}

f) (1p)

class Igelkott : public RundSak, public VassSak {
  int taggar;
public:
  Igelkott(double diameter, int vasshet, int taggar);
  virtual void peta();
};

g) (1p)

Igelkott::Igelkott(double diameter, int vasshet, int taggar)
  : RundSak(diameter), VassSak(vasshet) {
  this->taggar = taggar;
}

En alternativ lösning:

Igelkott::Igelkott(double diameter, int vasshet, int taggar)
  : RundSak(diameter), VassSak(vasshet), taggar(taggar) { }

h) (1p)

void Igelkott::peta() {
  VassSak::peta();
  RundSak::peta();
}

i) (1p) Vad blir utskrifterna om man i stället har följande main-funktion?

Aj!
Aj!
Rullerull!
Aj!
Rullerull!


Thomas Padron-McCarthy (Thomas.Padron-McCarthy@tech.oru.se) 20 november 2006