Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: C++: Luokat

Sivun loppuun

anttipanda [07.12.2004 18:38:51]

#

Yksi olio-ohjelmoinnin perusperiaatteista on tiedon piilottaminen eli kapselointi (encapsulation). Luokan tiedot piilotetaan muilta ohjelman osilta, ja luokasta luodun olion tietoihin päästään käsiksi julkisten (public) metodien (~=funktioiden) kautta. Tällainen menettely selkeyttää ohjelman rakennetta, ja pienentää yhden muutoksen aiheuttamia sivuvaikutuksia koodissa pakottamalla ohjelmoijan käyttämään tiettyjä reittejä tietojen käsittelyyn. Tavallisesti jäsenmuuttujat määritellään private-määreellä, luokan julkiset metodin public määreellä, ja vain luokan sisällä tarvittavat (mutta myös aliluokille periytyvät) metodit protected määreellä.

Tärkeä periaate, ehkäpä tärkein, on periytyminen. Luokasta voidaan periyttää aliluokkia, jotka perivät yläluokan ominaisuuksia tiettyjen sääntöjen mukaan (joko public, protected tai private -periytymistapa). Tähän liittyy läheisesti vielä termi polymorfismi, joka tarkoittaa että ylä-ja aliluokilla voi olla samannimisiä metodeita.

Metodien kuormittaminen tarkoittaa sitä että voidaan määritellä useita samannimisiä metodeita luokan sisään, mutta niiden tulee erota toisistan saamiensa parametrien perusteella. Esimerkissä muodostimet ovat kuormitettuja.

Tämä on vain pintaraapaisua olioparadigmaan, ja kehotan asiasta kiinnostuneita suuntaamaan tiensä kirjastoon. Lukeminen kannattaa tässäkin asiassa.

Seuraava esimerkki on kirjoitettu käyttäen visual studio 6.0:aa, ja toiminee ansi-standardia noudattavilla kääntäjillä.

#include <iostream>
using namespace std;
#include <string>


class TTyontekija
{
private:
	//luokan tietoja
	string etunimi;
	string sukunimi;
	int ika;

public:
	//luokan oletusmuodostin
	TTyontekija();
	//parametrillinen muodostin
	TTyontekija(string etunimi, string sukunimi, int ika);

	//muodostinta kutsutaan aina, kun luokasta luodaan uusi olio.
	//jos oliota luotaessa ei anneta mitään parametreja, käytetään tällöin
	//oletusmuodostinta. jos taas halutaan antaa parametreja se tehdään parametrillisella
	//muodostimella.

	//huom, jos paramertillinen muodostin on toteutettu,
	//on myös oletusmuodostin tehtävä itse.


	//metodit joilla asetetaan ja palautetaan kunkin jäsenmuuttujan tiedot

	//inline sana tarkoittaa, että kääntäjä korvaa metodikutsun käännösvaiheessa
	//metodin rungolla. tässä tapauksessa kun metodin toteutus on kirjoitettu
	//määrittelyn yhteyteen, inline sanaa ei varsinaisesti tarvita, mutta
	//ei siitä haittaakaan ole.
	inline void SetEtunimi(string etunimi) {this->etunimi = etunimi;}
	inline string ReturnEtunimi() {return etunimi;}

	inline void SetSukunimi(string sukunimi) {this->sukunimi = sukunimi;}
	inline string ReturnSukunimi() {return sukunimi;}

	inline void SetIka(int ika) {this->ika = ika;}
	inline int ReturnIka() {return ika;}

	void Tulosta();

};
//muodostimet on totetutettu tässä luokan rungon ulkopuolella
TTyontekija::TTyontekija()
{
	etunimi = "";
	sukunimi = "";
	ika = 0;
}
TTyontekija::TTyontekija(string etunimi, string sukunimi, int ika)
{
	this->etunimi = etunimi;
	this->sukunimi = sukunimi;
	this->ika = ika;
}
//tulostusmetodin toteutus
void TTyontekija::Tulosta()
{
	cout<<"Nimi: "<<etunimi<<" "<<sukunimi<<endl;
	cout<<"Ikä: "<<ika<<endl;
}


//periytetään työntekijä-luokasta uusi luokka, pomo-luokka.
//uusi luokka saa yläluokaltaan kaikki sen ominaisuudet, ja se voi käyttää
//suoraan yläluokan julkisia (public) ja suojattuja (protected) metodeita.
//private-määreellä määriteltyihin tietoihin sensijaan täytyy viitata yläluokan omien
//julkisten tai suojattujen metodien kautta. Esim. pomoluokasta ei ole suoraa
//pääsyä etunimi-muuttujaan, vaan muuttujan arvoon pääsee käsiksi kutsumalla työntekija-
//luokasta perittyjä SetEtunimi ja ReturnEtunimi -metodeita.
class TPomo : public TTyontekija
{
private:
	string osasto;
	int alaistenLkm;

public:
	//luokan oletusmuodostin
	TPomo();
	//kaksi erilaista parametrillista muodostinta
	TPomo(string etunimi, string sukunimi, int ika);
	TPomo(string etunimi, string sukunimi, int ika, string osasto, int alasitenLkm);

	inline void SetOsasto(string osasto) {this->osasto = osasto;}
	inline string ReturnOsasto() {return osasto;}

	inline void SetAlaistenLkm(int alaistenLkm) {this->alaistenLkm = alaistenLkm;}
	inline int ReturnAlaistenLkm() {return alaistenLkm;}

	void Tulosta();
};

//aina kun toteutetaan aliluokan muodostimia,
//tulee yläluokan muodostinta kutsua erikseen.
//muodostimet (ja hajottimet ja kopiomuodostimet) eivät
//periydy aliluokille.
TPomo::TPomo() : TTyontekija()
{
	osasto = "";
	alaistenLkm = 0;
}

TPomo::TPomo(string etunimi, string sukunimi, int ika) : TTyontekija(etunimi, sukunimi, ika)
{
	//yläluokan muodostimen kutsu voitaisiin suorittaa myös aliluokan muodostimen
	//rungossa, mutta kutsu 'otsikko'-rivillä on ohjelman suorituksen kannalta
	//tehokkaampi.

	//TTyontekija::TTyontekija(etunimi, sukunimi, ika)
}

TPomo::TPomo(string etunimi, string sukunimi, int ika, string osasto, int alaistenLkm) : TTyontekija(etunimi, sukunimi, ika)
{
	this->osasto = osasto;
	this->alaistenLkm = alaistenLkm;
}

void TPomo::Tulosta()
{
	TTyontekija::Tulosta();

	cout<<"Osasto: "<<osasto<<endl;
	cout<<"Alaisten lukumäärä: "<<alaistenLkm<<endl;
}


void main()
{
	//sitten yksinkertainen testiohjelma.
	//tehdään kaksi TPomo tyypin OLIOTA, joille annetaan
	//tietoja, ja tulostetaan ne sitten näytölle.

	//tässä on luotu olio käyttäen luokan oletusmuodostinta
	//aluksi pomon sisältö on tyhjiä merkkijonoja ja nollia.
	//kaikki tiedot asetetaan sitten erikseen (jos halutaan jotain
	//mielenkiintoisempaa tulostettavaa)
	TPomo pomo;

	pomo.SetEtunimi("Anselmi");
	pomo.SetSukunimi("Puolukka");
	pomo.SetIka(43);
	pomo.SetOsasto("Keittiö");
	pomo.SetAlaistenLkm(12);
	pomo.Tulosta();

	cout<<endl;

	//tässä on annettu oliolle tiedot jo muodostimessa
	//niitä ei ole pakko sitten asettaa myöhemmin.
	TPomo toinenPomo("Jussi","Suomalainen",25,"Maalaamo", 8);
	toinenPomo.Tulosta();

	cout<<endl;

	//ja sitten vielä yksi duunari.
	TTyontekija duunari("Möhkö", "Fantti", 22);
	//molemmilla luokilla on käytössä samannimisiä metodeita
	duunari.Tulosta();
}

Meitsi [08.12.2004 22:10:39]

#

Hmm...
"alasiten" lukumäärä... eiks sen pitäis olla "alaisten"

anttipanda [08.12.2004 22:38:39]

#

Pikkujuttu sadan vuoden päästä, mutta korjasinpas sen nyt kuitenkin.

Heikki [09.12.2004 07:19:02]

#

Ihan hyvä esimerkki.

Linkku [09.12.2004 07:44:50]

#

lainaus:

etunimi = "";

Ei kai stringejä tarvitse erikseen alustaa?

lainaus:

//suoraan yläluokan julkisia (public) ja suojattuja (private) metodeita.

pitäisi varmaankin olla protected

tn [09.12.2004 08:39:24]

#

lainaus:

lainaus:

etunimi = "";

Ei kai stringejä tarvitse erikseen alustaa?

Eipä tosiaan tarvitse. Ehkäpä tuossa haluttiin kuitenkin korostaa sitä, mikä arvo stringillä on olion luomisen jälkeen. Vaikutti kuitenkin muuten(kin) hyvältä esimerkiltä.

tejeez [09.12.2004 14:24:05]

#

HUVA JUTTU

Koipio-ohjelma [09.12.2004 18:40:46]

#

lainaus:

//suoraan yläluokan julkisia (public) ja suojattuja (private) metodeita.

pitäisi varmaankin olla protected

[/lainaus]

Ei, kyllä se on ihan private.
Minä kylläkin sanaa yksiyisiä metodeja...
But, once again, MINÄ tekisin niin...

Juice [09.12.2004 19:05:21]

#

Ei hassumpaa.

Kappas, on anttipandakin putkaan löytäny ;)

anttipanda [09.12.2004 21:09:12]

#

lainaus:

lainaus:

lainaus:

//suoraan yläluokan julkisia (public) ja suojattuja (private) metodeita.

pitäisi varmaankin olla protected

Ei, kyllä se on ihan private.

Tuossa oli minulla päässyt väliin virhe, määreen pitäisi olla nimeen omaan protected, ja sellaisiin yläluokan metodeihin/muuttujiin on aliluokalla suora pääsy. Private-määreellä määritellyt tunnukset ovat todellakin vain sen ja ainoastaan sen (jos friend-juttuja ei huomioida, en tiedä niistä mitään enkä siis käytä) luokan käytössä, jossa ne on määritelty.
Virhe korjattu.

lainaus:

Kappas, on anttipandakin putkaan löytäny ;)

Sellainen se on työmiehen lauantai. Ja maanantai:)

ZcMander [11.12.2004 10:56:46]

#

Hmm, pientä parannusta stringien myötä, oisit muuttanut kokoa, ja näin säästyis muistia, mutta pikkuistahan se on :D Lisäksi oisin tehnyt fuktiot paulauttamaan tietoja ja palauttamaan kokonimen (siis ei samassa fuktiossa)

anttipanda [11.12.2004 14:47:18]

#

Mutta tuollahan on metodit jotka palauttaa tiedot (kaikki nuo Return-alkuiset metodit). Ja en halunnut metodia joka palauttaa koko nimen, koska joku voisi haluta sukunimen ensiksi tai toisin päin. Luokkia ei pitäisi suunnitella vain tiettyä sovellusta silmällä pitäen, vaan yrittää tehdä niistä sellaisia, että niitä voi käyttää myöhemmin niitä enää muuttamatta.

Tuo Tulosta-metodi on tehty siksi, että voitaisiin huomata kahden samannimisen metodin olemassaolo kahdessa eri luokassa (polymorfismi), ja se miten yläluokan saman nimisiin metodeihin pitää viitata (esim. Tyontekija::Tulosta()). Ja tuo ei muuten ole tietojen palauttamista, vaan ihan tulostamista...

hmm, luulin että string -luokka varaa muistitilansa ihan itsestään sisältöönsä sopivaksi, ja että sille voi asettaa vain maksimipituuden. Voin olla väärässäkin. Mutta käytän string-luokkaa nimenomaan siksi, koska minua ei kiinnosta pelleillä tilanvarauksien kanssa, jos jokin luokka osaa tehdä sen ihan itsestään. Esim. c++:n listat, pinot ym...

freelove [25.02.2005 05:57:33]

#

muut kuin alkeistyypit kannattaa ehdottomasti välittää viitteenä.

void Bla(const std::string& something) {}

anttipanda [25.02.2005 14:14:17]

#

Onkohan sillä esim. stringin tapauksessa mitään merkitystä...? Moderneissa koneissa koneissa riittää kyllä muistia parille stringille jos se nyt siitä on kiinni. Koodin selkeys ja ymmärrettävyys on minusta EHDOTTOMASTI tärkeämpää kuin optimointi...
Kieltämättä joissain järjestelmissä on hyvä tehdä kompaktia koodia, mutta minulla ei ole kokemusta muista kuin windows-alustasta...

Mitä etuja tuosta viittausparametrin käytöstä muuten on? Muistia se säästää varmasti isojen olioiden kohdalla, mutta onko muuta mitä olisi syytä tietää?

Ja sitä paitsi kannattaa miettiä, missä alkaa hifistelemään; tällaisen perusesimerkin tapauksessa se tuskin on yhtään toivottavaa. Se vain hämää.

jsep [15.02.2006 08:04:23]

#

"...tiedon piilottaminen eli kapselointi (encapsulation). Luokan tiedot piilotetaan muilta ohjelman osilta, ja luokasta luodun olion tietoihin päästään käsiksi julkisten (public) metodien (~=funktioiden) kautta"

Toimiiko tämä periaate niinkuin ihan oikeasti - siis myös dll:issä?

Vrt.keskustelu: C / C++ ja Delphi / Pascal: C/C++ - globaalit c++ dll:ssä.. toimiiko?

TsaTsaTsaa [17.04.2007 16:30:08]

#

Alustuslista on ystävä rakentajien(=muodostimien) kanssa:

TTyontekija::TTyontekija(): etunimi(""), sukunimi(""), ika(0)
{}

TTyontekija::TTyontekija(string etunimi, string sukunimi, int ika): etunimi(etunimi), sukunimi(sukunimi), ika(ika)
{}

vehkis91 [17.06.2008 19:43:24]

#

Tsa... tuleeko tuosta nopeusetua, vai onko se muuten vaan kannattavampaa tehdä noin?

vehkis91 [21.09.2008 01:08:28]

#

Eikös funktiot ole automaattisesti "inline-tyyppiä", jos ne määritellään luokan sisällä?
Kertokaa toki jos olen väärässä.

Teuro [30.09.2008 07:53:46]

#

vehkis91: metodit eivät ole automaattisti inline tyyppisiä. Inline tarkoittaa sitä, että funktiokutsun paikalle kirjoitetaan funktion koodi, jolloin ohjelma tehostuu joskus huomattavastikin. Tämä tosin johtaa suurempaan ohjelman kokoon, ymmärrät varmaankin miksi.

vehkis91 [03.10.2008 17:09:00]

#

Juu tiesin sen, mutta luin jostain(?), että luokan sisässä määritellyt funktiot ovat inline funkkareita automaattisesti.

Class joku
{
private:
 string nimi;
 int ika;
public:

//Tämä olisi inline automaattisesti
 void asetaNimi(string nimi)
  {
   joku::nimi=nimi;
  }

  //Tässä taas pitää olla, koska sitä ei ole määrritelty luokan sisällä.
  inline void asetaIka(int ika);

};

Hengilö [23.05.2013 15:12:31]

#

Eikös mainin kannattaisi olla int-tyyppiä eikä voidi... Noh, olkoon, erittäin hyvä koodivinkki. :)


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta