Prev Next Up Title Contents Index

Perinnän esimerkki


Voitaisiin esimerkiksi rakentaa graafista kirjastoa ja haluttaisiin graafisille olioille seuraavia ominaisuuksia:

Kaikki edellä mainitut ominaisuudet ovat oliosta riippumattomia lukuunottamatta olion fyysistä piirtämistä näytölle. Voisimme aluksi kirjoittaa ominaisuudet pisteelle. Ympyrälle vastaavat ominaisuudet saisimme perimällä pisteen ominaisuudet. Ympyrälle tulee tietysti lisäksi ominaisuuksiin säteen käsittely. Tämä esimerkki ei kuitenkaan olisi IS-A -säännön mukaista, sillä eihän voida sanoa että "ympyrä on piste". Ohjelmateknisesti näin voitaisiin kyllä tehdä.

Ideaa voidaan kuitenkin jalostaa tekemällä ensin yleinen graafinen olio (caKuvio), jolla on "kaikkien" graafisten olioiden ominaisuudet. Tästä luokasta voidaan sitten periä muita graafisia luokkia, jolloin lisätään vain uuden luokan omat "hienoudet". Yleisestä graafisesta luokasta ei voi olla yhtään ilmentymää (instance), eli varsinaista tätä luokkaa olevaa oliota. Tällaista luokkaa sanotaan abstraktiksi luokaksi.

Aluksi suunnittelemme luokkahierarkian (c=class, a=abstract):

Click here for Picture

Kuva . Graafisten kuvioiden oliohierarkia

circle.cpp - malli perimisestä

	// circle.cpp, esimerkki perinnasta ja luokista/vl-96
	// Todellisen piirtämisen sijasta tulostetaan näyttöön keskipiste & koko
	// tulosta-metodi on vain tätä tarkoitusta varten!
	#include <stdio.h>
	
	//----------------------------------------------------------------------------
	class caKuvio {
	  int nakyy;                 // Sytytetty vai sammutettu
	  void vaihda_paikka(int nx,int ny)   { x = nx; y = ny;                      }
	protected:                   // Jos halutaan periä luokkaa, pitää jälkeläisten
	  int x,y;                   // käyttöön sallittujen attrib. olla protected
	public:
	  caKuvio(int ix=0, int iy=0) { vaihda_paikka(ix,iy); nakyy = 0;             }
	  virtual ~caKuvio()          {                                              }
	  int nakyvissa() const       { return nakyy;                                }
	  virtual void piirra() const = 0;    // Pure virtual-metodi => abstr. luokka
	  void sammuta()              { if ( nakyvissa()  ) { nakyy = 0;  piirra(); }}
	  void sytyta()               { if ( !nakyvissa() ) { nakyy = 1;  piirra(); }}
	  void siirra(int nx, int ny) {
	    if ( !nakyvissa() ) { vaihda_paikka(nx,ny); return; }
	    sammuta(); vaihda_paikka(nx,ny); sytyta();
	  }
	  virtual void tulosta(const char *s="") const {
	    printf("%-10s: %-10s (%02d,%02d)",nakyy?"Sytytetty":"Sammutettu",s,x,y);
	  }
	}; // caKuvio
	
	
	//----------------------------------------------------------------------------
	class caKuvioJollaSade : public caKuvio {
	  int koko(int nr) { r = nr; return 0;      }
	protected:
	  int r;                     // Uusi atribuutti, koko
	public:
	  caKuvioJollaSade(int ix=0,int iy=0,int ir=1) :
	    caKuvio(ix,iy), r(ir) {}
	  void muuta_koko(int nr) {  // Uutena ominaisuutena koon muuttaminen
	    if ( !nakyvissa() ) { koko(nr); return; }
	    sammuta(); koko(nr); sytyta();
	  }
	  virtual void tulosta(const char *s="") const {
	    caKuvio::tulosta(s);
	    printf( " r=%d",r);
	  }
	}; // caKuvioJollaSade
	
	//----------------------------------------------------------------------------
	class cPiste : public caKuvio {
	public:
	  cPiste(int ix=0, int iy=0) : caKuvio(ix,iy) {}
	  virtual ~cPiste()           { sammuta();                                   }
	  virtual void piirra() const { tulosta("Piste"); printf("\n");              }
	}; // cPiste
	
	//----------------------------------------------------------------------------
	class cYmpyra : public caKuvioJollaSade {
	public:
	  cYmpyra(int ix=0, int iy=0, int ir=1) : caKuvioJollaSade(ix,iy,ir) {}
	  virtual ~cYmpyra()          { sammuta();                                   }
	  virtual void piirra() const { tulosta("Ympyra"); printf("\n");             }
	}; // cYmpyra
	
	
	//----------------------------------------------------------------------------
	int main(void)
	{
	  printf("-------------------------------------------------\n");
	  cPiste p1,p2(10,20);  cYmpyra y1(1,1,2);
	  p1.sytyta();          // Sytytetty : Piste      (00,00)
	  p2.sytyta();          // Sytytetty : Piste      (10,20)
	  p1.siirra(7,8);       // Sammutettu: Piste      (00,00)
	                        // Sytytetty : Piste      (07,08)
	  y1.sytyta();          // Sytytetty : Ympyra     (01,01) r=2
	  y1.muuta_koko(5);     // Sammutettu: Ympyra     (01,01) r=2
	                        // Sytytetty : Ympyra     (01,01) r=5
	  return 0;             // Sammutettu: Ympyra     (01,01) r=5
	                        // Sammutettu: Piste      (10,20)
	                        // Sammutettu: Piste      (07,08)
	}
Piste on siis perinyt kaikki ominaisuudet graafiselta oliolta ja lisännyt tiedon siitä, miten piste "todellisuudessa" piirretään. Säteellinen olio on myös perinyt graafisen olion ominaisuudet, lisäten vielä olion kokoa kuvaavan suureen. Tästä taas ympyrä voidaan periä suoraan, kunhan kerrotaan miten tietyn kokoinen ympyrä piirretään. Selitämme jatkossa hieman enemmän perinnän syntaksia tämän esimerkin avulla.

Tehtävä 1.15 Virtual

Kokeile mitä tapahtuu ohjelmassa CIRCLE.CPP jos kuvion piirra - metodi määritelläänkin seuraavasti: void piirra() const {}. Entä jos tämän eteen lisätään vielä sana virtual.
Vaikuttaa virtual mitään tulosta- metodin edessä?

Tehtävä 1.16 Neliö

Lisää luokkahierarkiaan ja ohjelmaan CIRCLE.CPP luokka cNelio (säde on joko lävistäjän tai sivunpituuden puolikas, = pienin kuvion ympärille tai suurin kuvion sisälle piirretty ympyrä).

Tehtävä 1.17 Väri ja asento

Miten luokkahierarkia muuttuu, mikäli halutaan värillisiä kuvioita? Entä jos edelleen halutaan myös mustavalkoisia kuvioita? Entä jos mustavalkoisia kuvioita on yli 90% kaikista kuvioista. Entäpä jos halutaan eri asennoissa olevia kuvioita, kuitenkin siten, että pisteitä on 99% kaikista kuvioista?


Prev Next Up Title Contents Index