10. klasĖs - technomatematika+mk_pdf/10 sk.pdf · mp2->showmessage( ); // delete mp1; delete...

24
100 10. KLASĖS Klasės – C++ plėtinys C kalboje įvestoms struktūroms. Klasė praplečia struktūros galimybes apimdama ne tik duomenis – laukus, bet ir klasės duomenims būdingas funkcijas (klasės funkcijos paprastai vadinamos metodais). Be to, įvestos papildomos galimybės klasėje slėpti (inkapsuliuoti) atskirus klasės elementus – laukus ar metodus. Tam skirti specialūs raktažodžiai public, private ir protected. 9-ame skyriuje parašytą struktūrą Knyga galima pateikti kaip lygiai tas pat galimybes turinčią klasę taip: class Knyga { public: char autorius[ 80 ]; char pavadinimas[ 80 ]; char leidykla[ 80 ]; char isbn[ 80 ]; int puslapiai; int metai; }; Raktažodis public čia reiškia, kad kaip ir struktūros atveju visi klasės laukai yra matomi – laisvai pasiekiami iš už klasės ribų. Visa kita – kaip ir struktūrose: šis klasės skelbimas jokios kompiuterio atminties klasei dar neskiria; tik paskelbus klasės formato objektą, jam išskiriama reikiamos apimties atmintis, į kurią galima įrašyti norimas laukų reikšmes. Jei laukai atviri – tą galima padaryti pakoponenčiui, kaip struktūrose, o jei uždari (private) – tik per vadinamuosius metodus-konstruktorius arba specialius tarnybinius metodus. Klasės teturinčios tik laukus, dar vadinamos datoriais, o klasės tik iš metodų – funktoriais. 10.1 ĮVADINĖS ŽINIOS APIE KLASES 1 pavyzdys. Tipinė klasės struktūra yra tokia: #include <iostream> using namespace std; // class C { // klasės skelbimas private: // klasės laukai - uždari int data1; double data2; public: // klasės metodai - atviri void setData ( int d1, double d2 ) { data1 = d1; data2 = d2; } void getData ( ) { cout<<"Iveskite klases laukus\n"; cin>>data1>>data2;

Upload: others

Post on 18-Oct-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

100

10. KLASĖS

Klasės – C++ plėtinys C kalboje įvestoms struktūroms. Klasė praplečia struktūros galimybes apimdama ne tik duomenis – laukus, bet ir klasės duomenims būdingas funkcijas (klasės funkcijos paprastai vadinamos metodais). Be to, įvestos papildomos galimybės klasėje slėpti (inkapsuliuoti) atskirus klasės elementus – laukus ar metodus. Tam skirti specialūs raktažodžiai public, private ir protected. 9-ame skyriuje parašytą struktūrą Knyga galima pateikti kaip lygiai tas pat galimybes turinčią klasę taip:

class Knyga {

public:

char autorius[ 80 ];

char pavadinimas[ 80 ];

char leidykla[ 80 ];

char isbn[ 80 ];

int puslapiai;

int metai;

};

Raktažodis public čia reiškia, kad kaip ir struktūros atveju visi klasės laukai yra matomi – laisvai pasiekiami iš už klasės ribų. Visa kita – kaip ir struktūrose: šis klasės skelbimas jokios kompiuterio atminties klasei dar neskiria; tik paskelbus klasės formato objektą, jam išskiriama reikiamos apimties atmintis, į kurią galima įrašyti norimas laukų reikšmes. Jei laukai atviri – tą galima padaryti pakoponenčiui, kaip struktūrose, o jei uždari (private) – tik per vadinamuosius metodus-konstruktorius arba specialius tarnybinius metodus.

Klasės teturinčios tik laukus, dar vadinamos datoriais, o klasės tik iš metodų – funktoriais.

10.1 ĮVADINĖS ŽINIOS APIE KLASES 1 pavyzdys. Tipinė klasės struktūra yra tokia: #include <iostream>

using namespace std;

//

class C { // klasės skelbimas

private: // klasės laukai - uždari

int data1;

double data2;

public: // klasės metodai - atviri

void setData ( int d1, double d2 ) {

data1 = d1;

data2 = d2;

}

void getData ( ) {

cout<<"Iveskite klases laukus\n";

cin>>data1>>data2;

Page 2: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

101

}

void showData ( ) {

cout<<"Klases laukai: "<<data1

<<" "<<data2<<endl;

}

}; // kabliataškis!

//

int main( ) {

C c1, c2, c3; // objektų skelbimas

//

c1.setData ( 1, 2.3 ); // metodai kviečiami konkretiems objektams

c2.getData ( );

//

cout<<endl;

cout<<"Objektas c1\n"; c1.showData ( );

cout<<"Objektas c2\n"; c2.showData ( );

cout<<"Objektas c3\n"; c3.showData ( );

//

system("pause");

return 0;

}

Taigi, klasės skelbimas jokios atminties neskiria, tik nustato duomenų struktūrą ir parodo,

kokius veiksmus galima būtų atlikti su šiuos duomenimis. Klasės objektai c1, c2 ir c3 skelbiami ir apibrėžiami (t.y. jiems skiriama atmintis) startinėje funkcijoje. Klasės/objekto santykis yra lygiai toks, kaip vidinio C++ duomens formato/kintamojo arba struktūros/struktūros objekto. Objektai visada suformuojami specialiu metodu-konstruktoriumi. Šiame pavyzdyje mes tokio metodo neparašėme, todėl jį pagal numatymą sukuria pats kompiliatorius. Numatytuoju konstruktoriumi sukūrus objektus, jų laukų reikšmės yra atsitiktinės. Tuo galite įsitikinti patys, paleidę programą ir pažiūrėję, kokias laukų reikšmes ji atspausdins objektui c3. Baigiant programai darbą, visi klasės objektai sunaikinami – iš jų atimama atmintis. Tą darbą atlieka irgi specialūs metodai-destruktoriai, kurių šioje programoje taip pat neparašėme – juos automatiškai sukurs kompiliatorius. Destruktorius patiems rašyti reikia tik tada, kai naikinant objektą būtina ne tik atimti atmintį, bet ir atlikti kokius nors papildomus veiksmus (sakykim, grafinio elemento klasėje – išvalyti vaizdą kompiuterio ekrane) arba kai klasėje naudojami dinaminiai kintamieji.

Tradiciškai visi klasės laukai skelbiami uždarais, o prie jų prieiti galima tik per atvirus metodus. Tai ypač patogu kuriant klases, skirtas platesniam vartotojų ratui: paprastam klasės vartotojui būtina žinoti tik vadinamąją klasės sąsają – metodus-konstruktorius ir metodus, o visa kita nuo jo yra paslėpta ir todėl saugu. Be to, klasės kūrėjas išlaikydamas klasės sąsają tokią pačią, gali laisvai modifikuoti klasės vidų. Jei visas klasės turinys būtų atviras bet kuriam jos vartotojui, neišmanantis vartotojas galėtų daryti su tuo turiniu ką panorėjęs ir lengvai pažeisti klasės funkcionalumą. Tuo būdu, klasės vartotojas turi matyti vien tik tai, kas jam aktualu – klasės sąsają. Toks informacijos inkapsuliavimas – vienas iš pagrindinių objektinio programavimo principų.

Metodai kviečiami (dar sakoma: siunčiami pranešimai) tik konkrečiam objektui per taško operaciją. Metodai „mato“ visus klasės laukus, todėl metodams getData ir showData

Page 3: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

102

argumentų sąrašai yra tušti. Kaip matote iš šios programos, nė vienam objektui nėra iškviesti visi klasėje užrašyti metodai. To ir nereikia: metodai tik rodo, kokius veiksmus galima (bet toli gražu nebūtina) atlikti su klasės laukais. Programoje užrašėme tik tokius tarnybinius metodus, kuriuos turės dauguma mūsų klasių: laukų inicializavimo metodą, laukų įvedimo metodą ir laukų reikšmių parodymo metodą. Be abejo, reali klasė privalo dar turėti metodus, kurie su laukų reikšmėmis atliktų kokius nors prasmingus veiksmus.

Dėl klasių pavadinimų: patogu nusistatyti taisykles, pagal kurias įvardinsime visas klases. Prisilaikysime Java kalboje standartiškai taikomų pavadinimų taisyklių – visus savo klasių pavadinimus pradėsime didžiosiomis raidėmis, o visus klasių objektų pavadinimus – tik iš mažųjų raidžių. Be to, visų C++ bibliotekinių klasių pavadinimai pradedami mažosiomis raidėmis, todėl niekada nekils jų ir mūsų klasių vardų konfliktai.

10.2 METODAI-KONSTRUKTORIAI IR DESTRUKTORIAI

2 pavyzdys. Pirmąją programą galime papildyti metodais-konstruktoriais be argumentų ir su argumentais bei destruktoriumi:

#include <iostream>

using namespace std;

//

class C {

private:

int data1;

double data2;

public:

// konstruktoriai be argumentų ir su argumentais

C ( ): data1( 0 ), data2( 0. ) { }

C ( int d1, double d2 ): data1( d1 ), data2( d2 ) { }

// destruktorius

~C ( ) { };

// metodai

void setData ( int d1, double d2 ) {

data1 = d1;

data2 = d2;

}

void getData ( ) {

cout<<"Iveskite klases laukus\n";

cin>>data1>>data2;

}

void showData ( ) {

cout<<"Klases laukai: "<<data1

<<" "<<data2<<endl;

}

};

//

int main( ) {

Page 4: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

103

C c1( 1, 2.3 ), c2( 0, 0. ), c3;

//

cout<<endl;

cout<<"Objektas c1\n"; c1.showData ( );

cout<<"Objektas c2\n";c2.showData ( );

cout<<"Objektas c3\n";c3.showData ( );

//

system("pause");

return 0;

}

Kaip matote, panašiai kaip perkraunamų funkcijų atveju (žr. 5.6 skyrių), parašėme du

metodus-konstruktorius: be argumentų ir su argumentais. Konstruktorius pradedamas klasės pavadinimu, o prieš jį jokio grąžinamos reikšmės formato ar raktažodžio void neturi būti. Abiem atvejais klasės laukams reikšmės priskiriamos vadinamajame inicializavimo sąraše iškart po konstruktoriaus argumentų sąrašo, po dvitaškio, o metodų-konstruktorių kūnai tada lieka tušti – nors galima laukų reikšmes priskirti ir konstruktoriaus kūne. Tai turi tam tikrų pranašumų, kuriuos išnagrinėsime kalbėdami apie vykdymo meto klaidų apdorojimo galimybes išimčių mechanizmu.

Kaip iškviesti metodus-konstruktorius, rodo pirmasis startinės funkcijos operatorius: objektas c1 formuojamas laukams priskiriant reikšmes 1 ir 2.3, o objektų c2 ir c3 laukams priskiriamos nulinės reikšmės. Kadangi konstruojant c3 objektą joks argumentų sąrašas nepateikiamas, automatiškai sužadinamas metodas-konstruktorius be argumentų. Beje, jei programoje parašysime vien tik metodą-konstruktorių su argumentais, kompiliatorius automatiškai jau neparengs numatytojo metodo-konstruktoriaus be argumentų ir toks kaip programoje objekto c3 konstravimas būtų negalimas.

Metodas-destruktorius pradedamas taip pat klasės pavadinimu su prieš jį einančiu simboliu ~. Jei jokių papildomų veiksmų naikinant objektą atlikti nereikia, destruktoriaus kūnas gali likti tuščias. Destruktorius būtinas, pavyzdžiui, kai kuriant objektą laukui (-ams) skiriama dinaminė atmintis – žr. 4-ąjį programos pavyzdį.

3 pavyzdžio programa aiškiai rodo, kaip sukuriami programos objektai: į konstruktoriaus vidų įdėtas pranešimo spausdinimas. Programa – paprasta skaitiklio klasė, skaičiuojanti, kiek pakito klasės lauko reikšmė, didinama atskiru metodu. Vienas klasės jau ne void, grąžina klasės lauko reikšmę, todėl turi būti kviečiamas vykdomojo C++ operatoriaus viduje.

#include <iostream>

using namespace std;

//

class Counter {

private:

int count;

public:

//

Counter ( ): count( 0 ) {

cout<<"Vykdomas metodas-konstruktorius\n";

}

//

Page 5: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

104

~Counter ( ) { }

//

void increase ( ) {

count++;

}

//

int returnCount ( ) {

return count;

}

};

//

int main( ) {

//

Counter c1, c2;

//

cout<<"c1: "<<c1.returnCount( )<<endl;

cout<<"c2: "<<c2.returnCount( )<<endl;

c1.increase ( );

c1.increase ( );

c2.increase ( );

cout<<"c1: "<<c1.returnCount( )<<endl;

cout<<"c2: "<<c2.returnCount( )<<endl;

//

cout<<endl;

system("pause");

return 0;

}

Programos spausdiniai Vykdomas metodas-konstruktorius

Vykdomas metodas-konstruktorius

c1: 0

c2: 0

c1: 2

c1: 1

Toliau kalbėsime apie tokias programas, kur kompiliatoriaus numatytasis destruktorius

netinka ir tiesiog būtina parašyti savo destruktorių. Vienas tokių atvejų – kai klasės laukui skirta dinaminė atmintis. Tolesnį šio skyrelio tekstą praleiskite ir grįžkite prie jo tik susipažinę su rodyklės konstrukcija ir dinamine atmintimi.

Parašysime paprastutę klasę vien tik reikiamai destruktoriaus sintaksei parodyti. Tegu klasėje bus tik vienas char* formato laukas eilutės duomeniui saugoti. Laukui skirsime dinaminę atmintį.

4 pavyzdys: #include <iostream>

Page 6: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

105

#include <cstring>

using namespace std;

//

class Message{

private:

char* message;

public:

Message( const char* text = "Default message" ) {

cout<<"Konstruktorius\n";

message = new char[ strlen( text )+1 ];

strcpy( message, text );

}

//

~Message( ) {

cout<<"Destruktorius\n";

delete[ ] message;

}

//

void showMessage( ) const {

cout<<message<<endl;

}

};

//

int main( ){

Message mo( "The 1st message" );

Message* mp1 = new Message( "The 2nd message" );

Message* mp2 = new Message;

//

mo.showMessage( );

mp1->showMessage( );

mp2->showMessage( );

//

delete mp1;

delete mp2;

//

system( "pause" );

return 0;

}

Klasėje Message yra konstruktorius su argumentais, nukopijuojantis į klasės lauką message per argumentą ateinantį rodykle realizuotą tekstą. Šis argumentas yra konstantinis, t.y. konstruktoriaus kūne jo reikšmės niekaip keisti negalime, ir turi numatytąją reikšmę Default message, kuri būtų kopijuojama į lauką, jei jokio argumento kviečiant konstruktorių nebūtų pateikta. Konstruktoriaus viduje laukui message paskiriama text ilgio (ilgį sužinome funkcijos strlen pagalba) dinaminė atmintis, ir į ją nukopijuojama text reikšmė.

Startinėje funkcijoje sukuriami trys klasės objektai: vienas automatinis objektas (mo) ir du dinaminiai (mp1 ir mp2); visų trijų objektų laukų reikšmės parodomos metodu showMessage.

Page 7: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

106

O dabar apie destruktorius. Kai konstruktoriuje laukui paskiriama dinaminė atmintis, būtina parašyti destruktorių, kuris tą atmintį išlaisvintų, ir dar sunaikinti patį dinaminį objektą. Apie automatinius objektus: jei destruktoriuje atminties neišlaisvinti – nors kompiliatorius ir sunaikintų patį automatinį objektą (pavyzdžiui, išėjus už jo matomumo srities), dinaminė laukui paskirta atmintis vis tiek nebūtų grąžinta. Apie dinaminius objektus: tokį objektą sunaikinti galima tik išreikštai iškvietus operatorių delete – tik tada kviečiamas destruktorius (aišku, pačiame destruktoriuje turi būti numatytas lauko atminties išlaisvinimas).

Šioje programoje visos reikalingos sąlygos yra išlaikytos, tačiau jei dirbate su MS2010 programavimo terpe, paleidę programą matysite tik du destruktoriaus pranešimus Destruktorius. Šiuos pranešimus sąlygoja operatoriai delete mp1; ir delete mp2; . Automatinio objekto mo naikinimui, kai programa eikvoja mažai heap atminties, destruktorius kviečiamas pagal sudėtingas taisykles, kurių čia nenagrinėsime.

10.3 SU OBJEKTAIS DIRBANTYS METODAI. METODAI NE KLASĖS KŪNE. KOPIJUOJANTYSIS KONSTRUKTORIUS

Parašysime klasę angliškiems ilgio matams (pėdoms ir coliams; pėda yra 12 colių. Pėdos –

sveikojo tipo duomuo, o coliai gali įgyti realaus formato vertes) talpinti su metodais-konstruktoriais, destruktoriumi, tarnybiniais metodais ir vienu metodu dviems ilgiams sudėti. Ši dėkinga mokymuisi programos idėja pasiskolinta iš R. Lafore knygos. Vėliau programą keletą kartų perrašysime ir jos pagrindu išsiaiškinsime pačius įvairiausius objektinio programavimo aspektus.

Ilgių sudėties metodui teks perduoti du ilgių objektus. Šioje programos versijoje abu ilgius perduosime per metodo argumentų sąrašą. Sudedant angliškus ilgius, jei colių skaičius viršija 12, tenka didinti vienetu pėdų skaičių ir iš gauto colių skaičiaus atimti 12.

5 pavyzdys: #include <iostream>

using namespace std;

//

class Distance {

private:

int feet; // pėdos

double inches; // coliai

//

public:

Distance ( ): feet( 0 ), inches( 0. ) { }

Distance ( int ft, double in ): feet( ft ), inches( in ) { }

//

~Distance ( ) { }

//

void getDistance ( ) {

cout<<"Iveskite pedas ir colius\n";

cin>>feet>>inches;

}

Page 8: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

107

void showDistance ( ) {

cout<<"Pedos: "<<feet<<" coliai: "<<inches<<endl;

}

//

void addDistances ( Distance d1, Distance d2 ) {

inches = d1.inches + d2.inches;

feet = d1.feet + d2.feet;

if( inches >= 12. ) {

inches-= 12.;

feet++;

}

}

//

};

//

int main( ) {

//

Distance d1, d2;

Distance d3( 1, 2.3 );

//

d2.getDistance( );

//

cout<<"d1: "; d1.showDistance( );

cout<<"d2: "; d2.showDistance( );

cout<<"d3: "; d3.showDistance( );

//

d1.addDistances( d2, d3 );

//

cout<<"d1: "; d1.showDistance( );

//

cout<<endl;

system("pause");

return 0;

}

Paleidus programą ir reaguojant į jos užklausą įvedus skaičius 2 ir 11.2, gausime tokius spausdinius:

d1: Pedos: 0 coliai 0

d2: Pedos: 2 coliai 11.2

d3: Pedos: 1 coliai 2.3

d1: Pedos: 4 coliai 1.5

Visa programos sintaksė jau mums žinoma. Keblumų gali sukelti metodo addDistances

turinys. Pirmiausia, metodo argumentų sąraše yra du klasės Distance objektai. Kaip čia yra: klasė dar teberašoma, ir jau naudoja jos formato objektus? Iš tikrųjų jokių keblumų kompiliatoriui čia nekils, nes objektai formuojami startinėje funkcijoje, kai visas klasės apibrėžimas jau yra aiškus. Antra, metodo viduje taško operacijomis kreipiamasi į formaliųjų

Page 9: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

108

argumentų-objektų d1 ir d2 laukus feet ir inches, o kairėje prieskyros pusėje nurodyti tik atitinkamų laukų pavadinimai. Tai – vadinamojo darbinio objekto, kviečiančio patį metodą, laukai. Taigi, kas yra darbinis objektas paaiškėja tik startinėje funkcijoje: metodą kviečia objektas d1, suformuotas metodu-konstruktoriumi be argumentų ir todėl gaunantis nulines laukų vertes. Faktiškieji metodo argumentai-objektai d2 ir d3 atitinka formaliuosius argumentus d1 ir d2; duomenys perduodami reikšmės mechanizmu. Kadangi metodu-konstruktoriumi suformuotam objektui d2 įvedėme laukų reikšmes 2 ir 11.2, o objektas d3 suformuotas konstruktoriumi su argumentais 1 bei 2.3, rezultatas bus 4 pėdos ir 1,5 colio.

Tą patį dviejų angliškų ilgių sudėties metodą galime perrašyti kitaip: galima metodo kūne suformuoti laikiną Distance objektą – darbinio objekto ir per argumentų sąrašą ateinančio objekto sumą, bei jį sugrąžinti iš metodo.

6 pavyzdys – tuos pat rezultatus tiems pat duomenims teikianti naujai parašyta 5-ojo pavyzdžio programa.

#include <iostream>

using namespace std;

//

class Distance {

private:

int feet; // pėdos

double inches; // coliai

//

public:

Distance ( ): feet( 0 ), inches( 0. ) { }

Distance ( int ft, double in ): feet( ft ), inches( in ) { }

//

~Distance ( ) { }

//

void getDistance ( ) {

cout<<"Iveskite pedas ir colius\n";

cin>>feet>>inches;

}

void showDistance ( ) {

cout<<"Pedos: "<<feet<<" coliai: "<<inches<<endl;

}

//

void addDistances1 ( Distance d1, Distance d2 ) {

inches = d1.inches + d2.inches;

feet = d1.feet + d2.feet;

if( inches >= 12. ) {

inches-= 12.;

feet++;

}

}

//

Distance addDistances2 ( Distance d ) {

int ft = feet + d.feet;

Page 10: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

109

double in = inches + d.inches;

if( in >= 12. ) {

in-= 12.;

ft++;

}

return Distance( ft, in );

}

//

};

//

int main( ) {

//

Distance d1, d2;

Distance d3( 1, 2.3 );

//

d2.getDistance( );

//

cout<<"d1: "; d1.showDistance( );

cout<<"d2: "; d2.showDistance( );

cout<<"d3: "; d3.showDistance( );

//

d1 = d2.addDistances2( d3 );

//

cout<<"d1: "; d1.showDistance( );

//

cout<<endl;

system("pause");

return 0;

}

Programos rezultatai, jei atsakydami į užklausą įvesime 2 ir 11.2, bus lygiai tokie pat. Programoje parašėme du alternatyvius sudėties metodus: vieną palikome ankstesnį, tik pavadinimo gale gale pridėjome simbolį ‚1‘, o antrasis pavadintas addDistances2; kviečiamas tik antrasis metodas. Antrajame sudėties metode į tarpinių kintamųjų ft ir in ląsteles įrašome dviejų objektų atitinkamų laukų sumas pagal angliškų ilgio matų skaičiavimo taisykles. Metodas sugrąžina bevardį klasės Distance objektą, formuojamą tiesiog return operatoriuje. Todėl dabar sudėties metodas turi būti kviečiamas vykdomajame operatoriuje (čia – prieskyros operatoriuje): kreipinys d2.addDistances2( d3 ) sugrąžina bevardį objektą su reikiamomis laukų reikšmėmis, o prieskyra jį nukopijuoja į objekto d1 atitinkamus laukus.

Bet koks du klasės objektus jungiantis prieskyros operatorius, pavyzdžiui, d3 = d1; mums intuityviai suprantamas: laukiame, kad būtų nukopijuoti atitinkami laukai. Kompiliatoriui tai nėra taip aišku, todėl jis šiems veiksmams atlikti parengia numatytąjį kopijuojantįjį konstruktorių. Kol klasėje nenaudojama dinaminė atmintis, kopijuojantįjį konstruktorių perrašyti nėra reikalo; kai naudojama – reikalai tampa tikrai sudėtingi ir šį konstruktorių tiesiog privalome perrašyti, perduodami jam klasės objektą būtinai adresu. Kopijuojantįjį konstruktorių kviesti galima dvejopai: jau matyta forma prieskyros operatoriuje, ir taip: Distance d3( d1 ); . Kaip pačiam parašyti kopijuojantįjį konstruktorių – žr. šio skyrelio gale.

Page 11: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

110

Objektą grąžinantį metodą addDistances2, jei programoje nėra metodo-konstruktoriaus su argumentais ar išvis nesame parašę savų konstruktorių, galima parašyti ir sukuriant tarpinį klasės objektą bei tą objektą iš metodo grąžinant taip:

. . . Distance addDistances2 ( Distance d ) {

Distance temp;

temp.ft = feet + d.feet;

temp. in = inches + d.inches;

if( temp.in >= 12. ) {

temp.in-= 12.;

temp.ft++;

}

return temp;

}

. . . Dažnai, ypač kai metodų tekstas ilgas, patogu klasės viduje pateikti tik metodų prototipus,

o jų realizacijas – už klasės ribų. 7 pavyzdyje kaip tik ir parodytas toks programavimo stilius: čia perrašyta 6-ojo pavyzdžio programa:

#include <iostream>

using namespace std;

//

class Distance {

private:

int feet;

double inches;

//

public:

Distance ( ): feet( 0 ), inches( 0. ) { }

Distance ( int ft, double in ): feet( ft ), inches( in ) { }

//

~Distance ( ) { }

//

// Metodų prototipai

//

void getDistance( );

void showDistance( );

void addDistances1( Distance, Distance );

Distance addDistances2 ( Distance );

//

};

//

// Metodų realizacijos – už klasės ir funkcijų ribų

//

void Distance::getDistance ( ) {

Page 12: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

111

cout<<"Iveskite pedas ir colius\n";

cin>>feet>>inches;

}

void Distance::showDistance ( ) {

cout<<"Pedos: "<<feet<<" coliai: "<<inches<<endl;

}

void Distance::addDistances1 ( Distance d1, Distance d2 ) {

inches = d1.inches + d2.inches;

feet = d1.feet + d2.feet;

if( inches >= 12. ) {

inches-= 12.;

feet++;

}

}

Distance Distance::addDistances2 ( Distance d ) {

int ft = feet + d.feet;

double in = inches + d.inches;

if( in >= 12. ) {

in-= 12.;

ft++;

}

return Distance( ft, in );

}

//

//

int main( ) {

//

Distance d1, d2;

Distance d3( 1, 2.3 );

//

d2.getDistance( );

//

cout<<"d1: "; d1.showDistance( );

cout<<"d2: "; d2.showDistance( );

cout<<"d3: "; d3.showDistance( );

//

d1 = d2.addDistances2( d3 );

//

cout<<"d1: "; d1.showDistance( );

//

cout<<endl;

system("pause");

return 0;

}

Page 13: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

112

Taip programuojant, programos skaitytojui klasės struktūra ir idėja tampa žymiai aiškesnės. Beje, metodų realizacijas nebūtina pateikti prieš startinę funkciją; jas galėjome nukelti ir į pradinio programos failo pačią pabaigą.

Dabar kalbėsime apie tai, kaip parašyti kopijuojantįjį konstruktorių ir kada jį būtina parašyti. Tolesnį šio skyrelio tekstą praleiskite ir grįžkite prie jo tik susipažinę su rodyklių konstrukcijomis ir dinamine atmintim.

Pirmasis dalykas: kaip reikėtų perrašyti kopijuojantįjį konstruktorių. Panagrinėkime 8 pavyzdį:

#include <iostream>

#include <cstring>

using namespace std;

//

class Message{

private:

char message[ 81 ];

public:

Message( const char* text = "Default message" ) {

cout<<"Metodas-konstruktorius\n";

strcpy( message, text );

}

//

~Message( ) {

cout<<"Destruktorius\n";

}

//

Message( Message m ) { // kopijuojantysis konstruktorius

cout<<"Kopijuojantysis konstruktorius\n";

strcpy( message, m.message );

}

//

void showMessage( ) const {

cout<<message<<endl;

}

};

//

int main( ){

Message m1( "The 1st message" );

Message m2( m1 );

Message m3 = m2;

//

m1.showMessage( );

m2.showMessage( );

m3.showMessage( );

//

system( "pause" );

return 0;

Page 14: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

113

}

Iš pirmo žvilgsnio atrodo, kad taip parašytas kopijuojantysis konstruktorius turi veikti

nepriekaištingai. Tačiau: formalusis argumentas-klasės objektas į konstruktorių atiduodamas reikšmės mechanizmu, o tai reiškia, kad argumentą reikia nukopijuoti į tarpinę atminties sritį. Tam konstruktorius turi kviesti pats save perduodamas argumentą reikšme, o tam vėl reikia kopijuoti argumentą į tarpinę atmintį, ir t.t. – gaunam begalinę rekursinę grandinę. Beje, MS2010 kompiliatorius net ir deklaruotų sintaksės klaidą taip parašytam kopijuojančiajam konstruktoriui. Problemos sprendimas yra akivaizdus: turim konstruktoriaus argumentą perduoti adreso mechanizmu ir, pageidautina, dar vardan saugumo tą argumentą skelbti konstantiniu:

Message( const Message& m ) {

cout<<"Kopijuojantysis konstruktorius\n";

strcpy( message, m.message );

}

Taip parašyta programa veiks, o iš pranešimų konstruktorių viduje matysite, kaip iš tikrųjų

bus suformuoti visi klasės objektai: m1 – metodu-konstruktoriumi, m2 ir m3 – kopijuojančiuoju konstruktoriumi. Jei dirbate su MS2010 kompiliatoriumi, deja, destruktoriaus pranešimų nematysite.

Dabar pakalbėsime, kada būtina parašyti savo kopijuojantįjį konstruktorių. Atsakymas toks: kai klasės laukas (-ai) kuriami dinamiškai. Truputį pakeiskime 4-ojo pavyzdžio programą – 9 pavyzdys:

#include <iostream>

#include <cstring>

using namespace std;

//

class Message{

private:

char* message;

public:

Message( const char* text = "Default message" ) {

cout<<"Metodas-konstruktorius\n";

message = new char[ strlen( text )+1 ];

strcpy( message, text );

}

//

~Message( ) {

cout<<"Destruktorius\n";

delete[ ] message;

}

//

void showMessage( ) const {

cout<<message<<endl;

}

Page 15: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

114

};

//

int main( ){

Message m1( "The 1st message" );

Message m2 = m1; // kviečiamas numatytasis

// kopijuojantysis konstruktorius

m1.showMessage( );

m2.showMessage( );

//

system( "pause" );

return 0;

}

Objektas m2 formuojamas numatytuoju, kompiliatoriaus parengtu kopijuojančiuoju

konstruktoriumi. Kadangi klasės laukas yra rodyklė, paprasčiausiai į objekto m2 lauką message nukopijuojamas objekto m1 lauko message adresas – taigi abiejų objektų laukai-rodyklės rodys į tą patį atmintinės adresą. Kol programa tokia kaip parašyta, viskas gerai. Tačiau jei lauko reikšmė kuriame nors objekte būtų pakeista, tai atitinkamai pakistų ir visų nukopijuotų objektų laukų reikšmės. Dar blogiau, jei kuris nors iš kopijuotų objektų būtų sunaikintas išėjus iš jo matomumo ribų arba (dinaminiam objektui) delete operatoriumi: dabar likusiųjų objektų rodyklės rodytų į nežinia ką. Pabandykit su ta pat klase paleisti tokią startinę funkciją:

int main( ){

Message m1( "The 1st message" );

Message m2 = m1; // kviečiamas numatytasis

// kopijuojantysis konstruktorius

{ // Lokali matomumo sritis objektui m3

Message m3( m2 ); // kviečiamas numatytasis

// kopijuojantysis konstruktorius

m1.showMessage( );

m2.showMessage( );

m3.showMessage( );

} // objektas m3 sunaikinamas

//

m2.showMessage( ); // klaida!

//

system( "pause" );

return 0;

}

Mano kompiuteryje tokia programa dėl paaiškintų priežasčių visad sukelia nepataisomą

programos vykdymo meto klaidą. Klaidą galima ištaisyti papildžius klasę savo parašytu kopijuojančiuoju konstruktoriumi, kuriame klasės laukui būtų paskiriama dinaminė atmintis:

Message( const Message& m ) {

cout<<"Kopijuojantysis konstruktorius\n";

message = new char[ strlen( m.message ) +1 ];

Page 16: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

115

strcpy( message, m.message );

}

Dabar visi nukopijuotieji objektai turės savo dinaminę atmintį laukui ir bus nepriklausomi

vienas nuo kito. 10.4 MODIFIKATORIUS const KLASĖJE. STATINIAI LAUKAI IR METODAI Modifikatorių const klasėje galima taikyti trejopai: - Jei metodas neturi keisti klasės laukų reikšmių, jį patartina deklaruoti konstantiniu –

tokiu atveju programuojant netyčia padarius klaidą ir bandant keisti laukų reikšmes, iškart generuojama kompiliavimo klaida;

- Jei vardan programos efektyvumo metodui klasės objektus perduodame adreso mechanizmu, tačiau nežadame keisti tų objektų laukų reikšmių, patartina tuos objektus deklaruoti konstantiniais;

- Jei nežadame keisti suformuoto objekto laukų reikšmių, jį galima paskelbti konstantiniu.

Visus tris modifikatoriaus taikymo atvejus rasite perrašytoje 7-oje programoje. 10 pavyzdys:

#include <iostream>

using namespace std;

//

class Distance {

private:

int feet;

double inches;

//

public:

Distance ( ): feet( 0 ), inches( 0. ) { }

Distance ( int ft, double in ): feet( ft ), inches( in ) { }

//

~Distance ( ) { }

//

void getDistance( );

void showDistance( ) const; // konstantinis metodas

void addDistances1( const Distance&, const Distance& ); // konstantiniai

// adresu perduodami

Distance addDistances2 ( const Distance& ); // argumentai

//

};

//

//

int main( ) {

//

Page 17: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

116

Distance d1, d2;

const Distance d3( 1, 2.3 ); // konstantinis objektas

//d3.getDistance( ); // klaida: d3 keisti negalima

//d3 = d1; // klaida: d3 keisti negalima

//

d2.getDistance( );

//

cout<<"d1: "; d1.showDistance( );

cout<<"d2: "; d2.showDistance( );

cout<<"d3: "; d3.showDistance( );

//

d1 = d2.addDistances2( d3 );

//

cout<<"d1: "; d1.showDistance( );

//

cout<<endl;

system("pause");

return 0;

}

//

// Metodų realizacijos

//

void Distance::getDistance ( ) {

cout<<"Iveskite pedas ir colius\n";

cin>>feet>>inches;

}

void Distance::showDistance ( ) const { // konstantinis metodas

// feet++; // klaida: metodas konstantinis - laukų keisti negali

cout<<"Pedos: "<<feet<<" coliai: "<<inches<<endl;

}

void Distance::addDistances1 ( const Distance& d1, const Distance& d2 ) { //

// konstantiniai argumentai

//d1.feet++; // klaida: argumentas-objektas d1 konstantinis

//d2.inches+=10.; // klaida: argumentas-objektas d2 konstantinis

inches = d1.inches + d2.inches;

feet = d1.feet + d2.feet;

if( inches >= 12. ) {

inches-= 12.;

feet++;

}

}

Distance Distance::addDistances2 ( const Distance& d ) { // konstantiniai argumentai

//d.feet++; // klaida: argumentas-objektas d konstantinis

int ft = feet + d.feet;

double in = inches + d.inches;

if( in >= 12. ) {

Page 18: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

117

in-= 12.;

ft++;

}

return Distance( ft, in );

}

Programoje taip pat parodytos klaidingos situacijos, kurias gali „išgaudyti“ su const skelbiami metodai, argumentai ar objektai. Tam, lyginant su 7-ąja programa, papildomai įdėti ir užkomentuoti tie operatoriai, kurie sukeltų kompiliavimo klaidas.

Statiniai klasės laukai. Jei kuris klasės laukas paskelbtas su raktažodžiu static, visiems

klasės objektams, nepaisant kiek tų objektų būtų prikurta, jis bus bendras. Tuo būdu, kreipinys į tokį lauką objekto vardu tampa beprasmis (nors ir galimas), ir dažniausiai į jį kreipiamasi klasės vardu. Skirsis ir statinio lauko inicializavimo sintaksė: tai turi būti padaryta už klasės ir funkcijų ribų. Jei tokio lauko neinicializuoti, jam automatiškai priskiriama nulinė reikšmė.

Visus šiuos dalykus iliustruoja 11 pavyzdžio programa. Klasę Distance pakeisime taip, kad būtų skaičiuojama, kiek yra sukurta klasės objektų.

#include <iostream>

using namespace std;

//

class Distance {

private:

static int count; // statinis objektų skaitiklis

int feet;

double inches;

//

public:

Distance ( ): feet( 0 ), inches( 0. ) {

count++; // konstruojant objektą didinti jų kiekį

cout<<"Konstruktorius be argumentu: objektas Nr. "

<<count<<endl;

}

Distance ( int ft, double in ): feet( ft ), inches( in ) {

count++; // konstruojant objektą didinti jų kiekį

cout<<"Konstruktorius su argumentais: objektas Nr. "

<<count<<endl;

}

//

~Distance ( ) { }

//

void getDistance( );

void showDistance( ) const;

void addDistances1( const Distance&, const Distance& );

Distance addDistances2 ( const Distance& );

//

};

Page 19: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

118

//

//

int Distance::count = 0; // statinio lauko inicializavimas -

// už klasės ribų!

//

int main( ) {

//

Distance d1, d2;

const Distance d3( 1, 2.3 );

Distance d[10];

//

cout<<"d1: "; d1.showDistance( );

cout<<"d2: "; d2.showDistance( );

cout<<"d3: "; d3.showDistance( );

cout<<"d[9]: "; d[9].showDistance( );

//

cout<<endl;

system("pause");

return 0;

}

//

//

void Distance::getDistance ( ) {

cout<<"Iveskite pedas ir colius\n";

cin>>feet>>inches;

}

void Distance::showDistance ( ) const {

cout<<"Pedos: "<<feet<<" coliai: "<<inches<<endl;

}

void Distance::addDistances1 ( const Distance& d1, const Distance& d2 ) {

inches = d1.inches + d2.inches;

feet = d1.feet + d2.feet;

if( inches >= 12. ) {

inches-= 12.;

feet++;

}

}

Distance Distance::addDistances2 ( const Distance& d ) {

int ft = feet + d.feet;

double in = inches + d.inches;

if( in >= 12. ) {

in-= 12.;

ft++;

}

return Distance( ft, in );

}

Page 20: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

119

Startinėje funkcijoje sukuriami 13 objektų: d1 ir d2 metodu-konstruktoriumi be argumentų, d3 – konstruktoriumi su argumentais, o objektų vienmatis masyvas d – vėl konstruktoriumi be argumentų. Kuriant kiekvieną objektą bet kuriuo iš konstruktorių, statinio lauko count reikšmė didinama vienetu. Kad būtų aišku, kurie konstruktoriai kviečiami, spausdinamas atitinkamas pranešimas ir dar konstruojamo objekto numeris. Iš šių spausdinių aišku, kad count visiems objektams užima vieną ir tą pačią atminties ląstelę. Atkreipkite dėmesį, kur statinis laukas inicializuojamas ir kad į jį kreipiamasi klasės vardu. Kreipinys atskiro objekto vardu prasmės neturi. Įdėkit į klasę metodą count laukui parodyti:

int returnCount( ){

return count;

}

ir startinėje funkcijoje po visų cout‘ų įdėkit operatorių

cout<<"Objektu yra: "<<d[5].returnCount( )<<endl;

Pamatysit, kad spausdinamas objektų skaičius visada yra 13 kad ir kuriam kitam d masyvo objektui tas metodas būtų kviestas.

Statiniu galima paskelbti ir klasės metodą. Ypatingo skirtumo nuo įprastų metodų nėra, tik tokį metodą galima kviesti net ir nesukūrus nė vieno klasės objekto. Aišku, kreipinys į metodą turi būti tokiu atveju klasės vardu. Tai galėtų būti naudinga, pavyzdžiui, kai programoje reikia aptikti, ar yra „gyvų“ klasės objektų. Ankstesnį metodą returnCount skelbti statiniu ir kreiptis į jį galima taip:

static int returnCount( ){

return count;

}

. . .

cout<<"Objektu yra: "<<Distance::returnCount( )<<endl;

Bus spausdinamas tas pat objektų skaičius 13.

10.5 DRAUGIŠKOS FUNKCIJOS IR KLASĖS Jei funkcija nėra klasės metodas, ji neturi prieigos prie klasės(-ių) private laukų. Tai

nepatogu, jei funkcija turi dirbti su kelių klasių laukais. Patogus programinis įrankis prieigai

prie uždarų laukų atverti – skelbti funkcijas draugiškomis reikiamoms klasėms.

11 pavyzdys: išorinė funkcija sumuoja dviejų klasių privačius laukus. Kad ji turėtų prieigą

prie jų, ji skelbiama draugiška – friend, ir jos prototipai įdedami į abi klases.

#include <iostream>

using namespace std;

Page 21: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

120

//

class B; // pastaba

//

class A{

private:

int data;

public:

A( ): data( 10 ){ }

friend int friendlyfunc( A, B ); // prototipas su "friend"

};

//

class B{

private:

int data;

public:

B( ): data( 20 ){ }

friend int friendlyfunc( A, B ); // prototipas su "friend"

};

//

int friendlyfunc( A a, B b ){ // išorinė funkcija prieš main: kad būtų matoma

return (a.data + b.data );

}

//

int main( ){

A a;

B b;

//

cout<< "Suma yra: "<<friendlyfunc( a, b )<<endl;

//

system( "pause" );

return 0;

}

Programos rezultas aiškus: Suma yra: 30. Neaiškus programoje gali būti vienas operatorius: iškart po direktyvų kompiliatoriui

įrašyta eilutė class B; . Šis operatorius vien tik parodo kompiliatoriui, kad B yra klasė ir kad toliau programoje reikia ieškoti klasės apibrėžimo. Be šio operatoriaus būtų deklaruota klaida klasės A kūne esančiam draugiškos funkcijos prototipui: būtų neaišku, kas yra B.

Šis draugiškų funkcijų mechanizmas prieštarauja objektinio programavimo esminiams principams – duomenų slėpimui klasių viduje. Jį derėtų taikyti rezervuotai, gal tik programos derinimo metu.

Panaši ir draugiškos klasės idėja: tokios klasės metodai turi prieigą prie kitos klasės, kurioje ši paskelbta draugiškąja, uždarų laukų.

12 pavyzdys vien tik draugiškos klasės sintaksei parodyti:

#include <iostream>

using namespace std;

Page 22: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

121

//

class A{

private:

int data;

public:

A( ): data( 10 ){ }

friend class B; // draugiškos klasės skelbimas

};

//

class B{

public:

void m( A a ){

cout<<"Data: "<<a.data<<endl; // yra prieiga

}

};

//

int main( ){

A a;

B b;

//

b.m( a );

//

system( "pause" );

return 0;

}

10.6 ĮDĖTINĖS KLASĖS. VIDINĖS KLASĖS Klasių laukai gali būti kitų klasių objektai. Toks klasių komponavimas kartais vadinamas

klasių kompozicija. Sintaksiškai šioje konstrukcijoje naujas yra tik klasės-komponento konstruktorių kvietimas sudėtinėje klasėje; šie operatoriai pažymėti atitinkamais komentarais programos pavyzdyje.

13 pavyzdys. Perrašysime programą trikampio plotui skaičiuoti iš jo viršūnių koordinačių. Anksčiau tą pat užprogramavome taikydami struct sintaksę (9-ojo skyriaus 3-ioji programa). Iš pradžių parašysime klasę duomenims apie viršūnę Vertex su dviem double laukais koordinatėms x ir y, o trikampio klasę Triangle sukomponuosime iš trijų Vertex objektų. Programos tekstas dabar jau žymiai ilgesnis, kadangi klasėse surašysime visus konstruktorius, destruktorius bei įprastus tarnybinius metodus laukams įvesti ir išvesti (nors startinėje funkcijoje išnaudosime ne visą šį funkcionalumą). Klasėje Vertex papildomai teks parašyti metodus laukams grąžinti – antraip tie laukai klasės išorėje lieka nematomi ir nepasiekiami. Tai, kad struktūrų laukai atviri, o klasių – standartiškai uždari, kaip minėta, yra esminis struktūrų ir klasių skirtumas. Vertex klasės metodai yra atviri, todėl juos galime kviesti Triangle klasėje Vertex objektams (tai taip pat atžymėta komentarais).

#include <iostream>

Page 23: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

122

using namespace std;

//

class Vertex{

private:

double x,y;

public:

Vertex( ): x(0.), y(0.) { }

Vertex( double xx, double yy ): x(xx), y(yy) { }

~Vertex( ) { }

//

void getVertex( char c ){

cout<<"Virsunei "<<c<<" ";

cin>>x>>y;

}

void showVertex( char c ){

cout<<"Virsunes "<<c<<" x= "<<x<<" y= "<<y<<endl;

}

double rx( ){

return x;

}

double ry( ){

return y;

}

};

//

class Triangle{

private:

Vertex a,b,c;

public:

Triangle( ): a( 0.,0. ), b( 0.,0. ), c( 0.,0. ) { } // kviečiami Vertex konstruktoriai

// su argumentais

Triangle( double ax, double ay,

double bx, double by,

double cx, double cy ):

a( ax, ay ), b( bx, by ),c( cx, cy ) { } // Vertex konstruktoriai

~Triangle( ) { }

//

void getTriangle( ){

cout<<"\nIveskite virsuniu koordinates\n";

a.getVertex( 'a' ); // kviečiami Vertex metodai

b.getVertex( 'b' );

c.getVertex( 'c' );

}

void showTriangle( ){

cout<<"\nVirsuniu koordinates\n";

a.showVertex( 'a' ); // kviečiami Vertex metodai

b.showVertex( 'b' );

Page 24: 10. KLASĖS - Technomatematika+MK_PDF/10 sk.pdf · mp2->showMessage( ); // delete mp1; delete mp2; // system( "pause" ); return 0; } Klasėje Message yra konstruktorius su argumentais,

123

c.showVertex( 'c' );

}

//

double area( ){

return ( ( b.rx()*c.ry() - c.rx()*b.ry() // kviečiami Vertex metodai

-a.rx()*c.ry() + c.rx()*a.ry()

+a.rx()*b.ry() - b.rx()*a.ry() )/2. );

}

};

//

int main( ) {

Triangle t( 0.,0., 5.,0., 0.,1.);

//

t.showTriangle( );

//

cout<<"\nTrikampio plotas "<<t.area()<<endl;

//

system("pause");

return 0;

}

Pabandykit paleisti programą ir formuoti trikampio objektą(-us) kitaip, metodu-konstruktoriumi be argumentų ir laukų įvesties metodais.

Vidinės klasės – klasės, apibrėžiamos kitos klasės viduje. Tą galima padaryt ir public, ir

private sekcijose. Tai – dar viena duomenų slėpimo, inkapsuliavimo galimybė. Ankstesnėje programoje galima be jokių pakeitimų klasės Vertex tekstą parašyti klasės Triangle vienoje kurioje sekcijoje – kadangi yra Vertex laukų grąžinimo metodai, viskas veiks. Prasmingus pavyzdžius su vidinėmis klasėmis pateiksime 15-ame skyriuje. Ten bus kalbama apie klaidų apdorojimo mechanizmą, ir visas klaidų klases apiforminsime kaip vidines klases.