Cpp funktion kuormitus
Mureakuha
Tässä luvussa opitaan hieman lisää funktioiden kuormituksesta esimerkiksi konstruktorin kuormituksesta ja oletusarvoargumenttien käytöstä.
Sisällysluettelo |
Konstruktorifunktion kuormitus
Konstruktorifunktion kuormitus on hyvin tavallista (Destruktorin kuormitus on mahdotonta). Kontruktoreita kuormitetaan pääasiassa kahdesta syystä: joustavuus ja lisätuki taulukoiden käsittelylle.
Kun olioita määritellään, konstruktoreista on aina löydyttävä vastaava, jotta määrittely olisi mahdollista. Jos luokan konstruktori haluaa esim. yhden argumentin, ei voida määritellä oliota kyseiseen luokkaan siten, että määrittelyssä ei olisi argumenttia. Edelleen yksittäinen olio alustetaan huomattavasti useammin kuin taulukollinen.
Esimerkki: Oliolle o1 annetaan alkuarvo, oliolle o2 ei anneta eli luokassa on oltava ainakin 2 erilaista konstruktoria, edelleen oliot o3 ja o4 on määritelty taulukoiksi ja o4:lle annetaan alkuarvot ja vielä lopuksi, dynaamista taulukkoa ei voida alustaa, joten on käytettävä konstruktoria, joka ei ota argumetteja:
#include <iostream> class myclass { int x; public: myclass() { cout << "konstruktori 1\n"; x = 0; } myclass( int n ) { cout << "konstruktori 2\n"; x = n; } int getx() { return x; } }; main() { myclass o1(10); myclass o2; cout << "o1: " << o1.getx() << '\n'; cout << "o2: " << o2.getx() << '\n'; myclass o3[5]; myclass o4[5] = {1,2,3,4,5}; cout << "o3: "; for (int i=0; i<5; i++) cout << o3[i].getx() << ' '; cout << '\n'; cout << "o4: "; for (i=0; i<5; i++) cout << o4[i].getx() << ' '; cout << '\n'; myclass *o5; o5 = new myclass [5]; if (!o5) { cout << "Varausvirhe\n"; return 1; } for (i=0; i<5; i++) o5[i] = o4[i]; for (i=0; i<5; i++) cout << "o5["<< i << "] = " << o5[i].getx() << "\n"; return 0; }
Kun konstruktori on kuormitettu sopivasti, ohjelmoija voi alustaa olionsa parhaimmin hänelle sopivalla tavalla. Seuraavassa päivämäärän tallettava luokka hyväksyy konstruktoriin päivämäärän joko merkkijonona tai kolmena kokonaislukuna:
#include <iostream> #include <stdio.h> class dateT { int day, month, year; public: dateT( char *str ) { sscanf( str, "%d%*c%d%*c%d", &day, &month, &year ); } dateT( int d, int m, int y ) { day = d; month = m; year = y; } void show() { cout << day << '/' << month << '/' << year << "\n"; } }; main() { dateT d1("11/1/92"); dateT d2("13.2.96"); dateT d3( 14, 2, 96 ); d1.show(); d2.show(); d3.show(); return 0; }
Funktio int sscanf(const char *buffer, const char *format[, address, ...]) lukee formatoidusti syötteen merkkijonosta. %*c formaatissa tarkoittaa, että luetaan merkki, mutta merkki heitetään roskiin.
Oletusarvoargumentit
C++:ssa on ominaisuus, joka on sukua funktioiden kuormitukselle: oletusarvoargumentti, jonka avulla argumentille voidaan antaa oletusarvo, jos kyseinen argumentti jätetään antamatta funktiokutsussa.
Esim. void f( int a=0, int b=0, int c=0);
Mikäli kutsussa ei anneta argumentille arvoa, käytetään argumentille arvoa 0.
Funktiota f voidaan kutsua neljällä eri tavalla:
f(); // a=0, b=0, c=0 f( 10 ); // a=10, b=0, c=0 f( 10, 5 ); // a=10, b=5, c=0 f( 10, 5, 3 ); // a=10, b=5, c=3
ESIMERKIKSI SEURAAVAT KUTSUT EIVÄT OLE SALLITTUJA:
f( , , 3 ); f(10, , 3); f( , 5, ); f( , 5, 3 );
Vastaavasti funktio voidaan määritellä esimerkiksi seuraavasti:
void f2( int a, int b=0, int c=0); // 1. argumentti on annettava void f3( int a, int b, int c=0); // 1. ja 2. argumentti on annettava
Mahdolliset kutsut:
f2( 10 ); f2( 10, 5 ); f2( 10, 5, 3 ); f3( 10, 5 ); f3( 10, 5, 3 );
Oletusarvoja voidaan antaa vain siten, että lopusta päin jätetään argumentteja antamatta.
Oletusarvot määritellään joko funktion prototyypissä tai itse funktion otsikko-osassa mutta ei molemmissa.
Argumenttien oletusarvot voivat olla vakioita tai globaaleja muuttujia mutta eivät paikallisia muuttujia tai muita argumentteja. Esimerkiksi seuraava ei käy:
void f( int a=0, int b=a, int c=b);
Esimerkki:
#include <iostream> double box_area( double length, double width = 0 ) { if (!width) width = length; return width*length; } main() { cout << "10 x 5.8 box has area: "; cout << box_area( 10, 5.8 ) << '\n'; cout << "10 x 10 box has area: "; cout << box_area( 10 ) << '\n'; return 0; }
box_area-funktio oltaisiin voitu toteuttaa myös kuormittamalla:
double box_area( double length ) { return length*length; } double box_area( double length, double width ) { return width*length; }
HUOM: Jos määritellään dynaaminen oliotaulukko new:n avulla, luokkaan on tehtävä konstruktori, jossa ei ole lainkaan argumentteja, oletusarvoargumenttien käyttö ei ole riittävää tällaiselle tapaukselle.
Esimerkki:
#include <iostream> #include <ctype.h> const int ignore = 0; const int upper = 1; const int lower = 2; void print( char *s, int how = -1 ) { static int oldcase = ignore; if (how<0) how = oldcase; while (*s) { switch (how) { case upper: cout << (char) toupper(*s); break; case lower: cout << (char) tolower(*s); break; default: cout << *s; } s++; } oldcase = how; } main() { print("Hello There\n", ignore); // Tulostuu: Hello There print("Hello There\n", upper); // Tulostuu: HELLO THERE print("Hello There\n"); // Tulostuu: HELLO THERE print("Hello There\n", lower); // Tulostuu: hello there print("Hello There\n"); // Tulostuu: hello there return 0; }
Kuormitusepämääräisyydet
Automaattiset konversiosäännöt aiheuttavat joskus ongelmia, epämääräisyyksiä.
Jos funktiota kutsutaan arvolla, joka ei ole samaa tyyppiä kuin funktio haluaisi argumentikseen, mutta tyyppi on yhteensopiva funktion tyypin kanssa, kääntäjä tekee automaattisesti tyyppimuunnoksen. Esimerkiksi vaikka putchar-funktion argumentti on määritelty int-tyyppiseksi, funktiota voi kutsua char-tyyppisellä arvolla: putchar( 'a' ); Kuormitustilanteissa automaattiset tyyppikonversiot voivat aiheuttaa ongelmia.
#include <iostream> float f( float i ) { return i/2.0; } double f( double i ) { return i/3.0; } main() { float x = 10.09; double y = 10.09; cout << f(x); cout << f(y); cout << f(10.09); // kutsuu double versiota cout << f(10); // käännösvirhe return 0; }
Compiling NONAME00.CPP:
Error NONAME00.CPP 20: Ambiguity between 'f(float)' and 'f(double)' in function main()
Kyseinen virhe tulee viimeisestä f:n kutsusta. Kääntäjä ei tiedä kumpaa kutsuisi.
#include <iostream> void f( unsigned char c ) { cout << c; } void f( char c ) { cout << c; } main() { f('c'); f(86); // virhe return 0; } //Compiling NONAME00.CPP: //Error NONAME00.CPP 16: Ambiguity between 'f(unsigned char)' and 'f(char)' in function main()
#include <iostream> int f( int a, int b ) { return a+b; } int f( int a, int &b ) // referenssi, 1. virhe { return a-b; } main() { int x=1, y=2; cout << f(x, y); // 2. virhe return 0; } // Compiling NONAME00.CPP: // Error NONAME00.CPP 8: 'f(int,int &)' cannot be distinguished from 'f(int,int)' // Error NONAME00.CPP 16: Ambiguity between 'f(int,int)' and 'f(int,int &)' in function main()
#include <iostream> int f( int a ) { return a*a; } int f( int a, int b = 0 ) // virhe { return a*b; } main() { cout << f( 10, 2 ); cout << f( 10 ); // virhe return 0; } // Compiling NONAME00.CPP: // Error NONAME00.CPP 16: Ambiguity between 'f(int)' and 'f(int,int)' in function main()
Kuormitetun funktion osoite
Jos funktion on void funktio( void ), niin kyseisen funktion osoite on funktio. Jos p on määritelty funktion osoitteen tyyppiseksi, voidaan tehdä sijoitus p = funktio;
Mitenkäs sitten kuormitustilanteissa, samalle nimelle on määritelty useita funktioita. Asia on yksinkertainen: riippuen siitä millaiseksi osoite p on määritelty, otetaan eri versiot funktiosta.
Esimerkkejä funktioiden osoitteiden määrittelyistä:
| void (*fp) ( void ); | fp on osoitin funktioon, joka ei palauta mitään ja jolle ei välitetä lainkaan argumentteja. |
| void (*fp) ( int ); | fp on osoitin funktioon, joka ei palauta mitään ja jolle välitetään argumenttina yksi integer. |
| int (*fp) ( void ); | fp on osoitin funktioon, joka palauttaa integerin ja jolle ei välitetä lainkaan argumentteja. |
| void (*fp) ( float, int ); | fp on osoitin funktioon, joka ei palauta mitään ja jolle välitetään argumenttina float-tyyppinen argumentti ja integer. |
| int *(*fp) ( float, int ); | fp on osoitin funktioon, joka palauttaa osoittimen integeriin ja jolle välitetään argumenttina float-tyyppinen argumentti ja integer. |
| int *(*fp) ( float*, int* ); | fp on osoitin funktioon, joka palauttaa osoittimen integeriin ja jolle välitetään argumenttina osoitteet float-tyyppiin ja integeriin. |
#include <iostream> void space( int count ) { for ( ; count; count-- ) cout << ' '; } void space( int count, char ch ) { for ( ; count; count-- ) cout << ch; } main() { void (*fp1) ( int ); void (*fp2) ( int, char ); fp1 = space; fp2 = space; cout << "\n<"; fp1( 22 ); cout << "><"; fp2( 22, 'x' ); cout << ">\n"; return 0; }
Kopio lisenssistä (englanniksi) löytyy täältä.
Alkuperäinen (c) Petteri Hämäläinen
