Løsningsforslag til oppgave 3-1 i OOP-boka.


Advarsel!

Det gies ikke noen garanti for at all kildekode er riktig. Den har ikke vært prøvd kompilert. Det som er, er bare eksempel på hvordan ting kunne vært gjort.

Jeg påstår heller ikke at måten jeg har løst problemet på er den beste, riktigste, mest effektive eller mest elegante måten å gjøre det på. Det er skrevet for å gi en liten pekepinn om hva det vil si å programmere objektorientert.


Først får en lese nøye gjennom oppgaveteksten for å se hva en skal gjøre.

Vi skal lage et forfatter-register. Hver forfatter har noen bøker de har skrevet. Oppgaven sier også noe om hva vi skal ha tak i fra registeret:

Først får vi se litt nærmere på det vi skal gjøre. Jeg bruker å lage en skisse av hovedprogrammet. Der lager jeg menyer for hver ting jeg skal løse i oppgaven, nogenlunde slik:


/********************************************************************\
 * File        : bib.cpp
 * Purpose     : Lage et forfatter-register for et bibliotek, opg 3-1 i OOP-boka
 * Author      : Per Gunnar Hansø (pergh@colargol.stud.idb.hist.no)
 * Language    : C++
 * Last edited : 07.05.95
\*******************************************************************/

#include <iostream.h>

void main()
{
  //Skrive hva programmet gjør
  //lese inn registeret
  //while (!Ferdig) {
    //skriv meny
    switch (Valg) {
      case SkrivInnForfatter       : SkrivInnForfatter(); 
                                     break;
      case SkrivInnBokTilForfatter : SkrivInnBokTilForfatter();
                                     break;
      case FinnForfatter           : FinnForfatter();
                                     break;
      case FinnBok                 : FinnBok();
                                     break;
      case FinnBøkerTilForfatter   : FinnBøkerTilForfatter();
                                     break;
      default : cerr << endl << Valg << " er ikke et gyldig valg!" << endl;
    };//switch
    Ferdig = (Valg == Avslutt);
  }//while
  //Lagre registeret
  //Skriv avsluttnings-hilsen
}//main


Dette er første utkast til løsning. Nå trenger vi et register som gir oss muligheten til å gjennomføre disse operasjonene.

Vi stiller noen spørsmål:

Hvis vi vil ha dette over på papir, blir det noe slikt:

Det som er kallt liste kan vi erstatte med tabeller. Siden dette er ei skisse, har jeg ikke skrevet noen bunn på klassene så jeg kan legge til ting senere når jeg trenger det.

Så får vi se hvilke funksjoner vi trenger i klassene. Vi trenger alltid tilgangsfunksjoner, Sett() og Finn() for hvert datamedlem. Dessuten er dette så mye data at vi bør sette av plass i det frie lageret. Da må vi ha med konstruktører også. Standardkonstruktør og destruktør er en selvfølge. For å være sikker på at ingen gjør noe galt med klassene, lager vi kopierings-konstruktør og operator = () også. Hvis en klasse er for stor til at det har noen hensikt å kopiere den, bør vi gjøre de to funksjonene private så ingen får gjøre det.

Klassen bok er en 'grunnklasse'. Den er ikke noen holder av andre ting. Derfor er det liten sjanse for at vi trenger å forandre den etter at vi har laget første utkast. Det som er skrevet i uthevet skrift, er ting jeg har som standard på alle klasser jeg lager.


/********************************************************************\
 * File        : bok.h
 * Purpose     : Lage en klasse som holder tittel og ant. eks. av en bok
 * Author      : Per Gunnar Hansø (pergh@colargol.stud.idb.hist.no)
 * Language    : C++
 * Last edited : 07.05.95
\*******************************************************************/
#include <iostream.h>
#include "tekst.h" 

class bok 
{
  private:
    tekst Tittel;
    int AntEksemplarer;
  public:
    bok();
    virtual ~bok();
    bok(const bok& Inn);
    bok(const char* InnTittel, int InnEksemplarer);
    bok(const tekst& InnTittel, int InnEksemplarer);
    bok& operator = (const bok& Inn);
    const tekst& FinnTittel() const;
    bool SettTittel(const tekst& InnTittel);
    int FinnAntEksemplarer() const;
    bool SettAntEksemplarer(int InnEksemplarer);
};

ostream& operator << (ostream& Ut, const bok& Bok);
istream& operator >> (istream& Inn, bok& Bok);

Grunnen til at jeg ikke har laget operatorer som >, < og ==, er at det ikke gir noen direkte mening å sammenligne to bøker. Skal en sammenligne to bøker på tittel, så får en ta FinnTittel() og sammenligne resultatet. Da er det ikke tvil om hva som blir sammenligna.

Når det er noe jeg skal skrive til fil, så lager jeg bestandig operatorene << og >> for hver klasse. De gjør det enkelt å lagre en forekomst til fil.

Nå som vi er ferdige med klassen bok, kan vi ta å snuse litt mere på forfatter.


/********************************************************************\
 * File        : forf.h
 * Purpose     : Klasse av forfatter som skal brukes i et forf. reg.
 * Author      : Per Gunnar Hansø (pergh@colargol.stud.idb.hist.no)
 * Language    : C++
 * Last edited : 07.05.95
\*******************************************************************/
#include <iostream.h>
#include "bok.h"
#include "navn.h" 

class forfatter
{
  private:
    bok* Boeker;
    navn Navn;
  public:
    forfatter();
    virtual ~forfatter();
    forfatter(const forfatter& Inn);
    forfatter& operator = (const forfatter& Inn) const;
    const bok& FinnBok(const tekst& Tittel) const;
    bool LeggTilBok(const bok& Inn);
    const navn& FinnNavn() const;
    bool SettNavn(const navn& Inn);
);

ostream& operator << (ostream& Ut, const forfatter& Forf);
istream& operator >> (istream& Inn, forfatter& Forf);

Legg merke til at to av tilgangs-funksjonene har annet navn enn det vi er vant til. FinnBok() har et argument, og den motsatte funksjonen heter LeggTilBok(), og ikke SettBok() som vi er vant til. Slik er det nesten alltid med tilgangsfunksjoner til holdere.

Det ser jo greit ut som vi har det, men det mangler noen tilganger. Disse blir kallt iterator-funksjoner. Med dem kan du gå gjennom alle datamedlemmene i en holder. Iterator-funksjoner kan fx. se slik ut:


class forfatter
{
  public:
    //Hvis vi har lenka lister, er dette standard tilgang:
    const bok& FinnFoersteBok() const;
    const bok& FinnNesteBok() const;

    //eller hakket enklere hvis vi har tabeller:

    int FinnAntBoeker() const;
    const bok& FinnBok(int Index) const;
};

Nå har vi laget oss adgang til det meste... la oss gå videre til neste klasse, selve registeret.


/********************************************************************\
 * File        : forfreg.h
 * Purpose     : Lage et forfatter-register for et bibliotek
 * Author      : Per Gunnar Hansø (pergh@colargol.stud.idb.hist.no)
 * Language    : C++
 * Last edited : 07.05.95
\*******************************************************************/
#include <iostream.h>
#include "bok.h"
#include "forfatter.h"
#include "navn.h" 

class forfatterregister
{
  private:
    forfatter* Reg;
    forfatterregister(const forfatterregister& Inn);
    operator = (const forfatterregister& Inn);
  public:
    forfatterregister();
    virtual ~forfatterregister();
    const forfatter& FinnForfatter(const tekst& Navn) const;
    bool LeggTilForfatter(const forfatter& Inn);

    //og den iterative tilgangene. Tar bare med dem for tabeller.
    int FinnAntForfattere() const;
    const forfatter& FinnForfatter(int Index) const;

    //Disse funksjonene er løsningen på oppgaven vår
    bool LeggBokTilForfatter(const forfatter& Forf, const bok& Inn);
    const forfatter& FinnForfatter(const bok& Bok) const;
    const bok& FinnBok(const char* Tittel) const;

);

ostream& operator << (ostream& Ut, const forfatterregister& Freg);
istream& operator >> (istream& Inn, forfatterregister& Freg);

Her er de ferdige implementasjons-filene. En del av feilsjekkinga har blitt veldig dårlig og ineffektiv. Hvis dette var et program som skulle brukes, måtte en gjort noe med det.

Filene ble laget i rekkefølgen de står i, unntatt bib.cpp, som bygger på det som er skissert før.

Den ferdige implementasjonen er litt forskjellig fra de .h-filene vi laget før. Blandt annet har det i hver holder-klasse kommet to variabler: En som sier hvor stor tabellen er (AntAvsatt), og en som sier hvor mange plasser i tabellen som egentlig er brukt (AntForfattere fx.).

En spesiell ting er måten jeg har brukt operatorene << og >> på. Hvis en ser godt etter er de "motsatte". Den ene skriver det den andre leser. Dette gjør det enkelt å bruke filer (som vist i hovedprogrammet).

En annen ting er hvordan minnet blir behandlet. Det som settes av i konstruktøren, frigjøres i destruktøren. I denne oppgaven er det ganske enkelt å holde styr på hva en har i det frie lageret, men i større oppgaver blir det fort rot, og minnelekasjer er vanskelige å finne.


Made avaliable by Per Gunnar Hansø
Updated: 13.11.95.