Cpp operaattorin kuormitus
Mureakuha
Tässä luvussa opitaan kuinka kuormitetaan operaattoreita. Operaattorin merkitys voidaan määrittää erikseen jokaiselle luokalle.
Sisällysluettelo |
Operaattorin kuormitus perusteet
Operaattoreiden kuormitus on yksi joukko funktioiden kuormituksesta. Muutama ero funktioiden kuormitukseen nähden kuitenkin on. Eräs ero on siinä, että operaattorin kuormitus liittyy aina johonkin luokkaan. Kun operaattoria kuormitetaan, mitään sen vanhoista ominaisuuksista ei poistu. Operaattori saa ainoastaan lisäominaisuuksia, kun sitä käytetään sellaisen luokan yhteydessä, johon operaattorin lisäominaisuudet on määritelty. Kun operaattoria kuormitetaan, luodaan operaattorifunktio. Useimmiten operaattorifunktio on luokan jäsenfunktio tai friend-funktio. Jäsenoperaattorifunktion ja friend-operaattorifunktion välillä on pieni ero.
Jäsenoperaattorifunktion muoto:
palautus_tyyppi luokan_nimi::operator# ( argumenttilista ) { muodostettava operaattori }
Usein palautus_tyyppi on sama kuin luokan_nimi vaikkakin se voi olla mikä tahansa tyyppi. Merkin # kohdalla on kyseisen kuormitettavan operaattorin merkki, esimerkiksi, jos kuormitetaan + operaattoria, funktion nimi on operator+.
Operaattoreiden kuormitukseen liittyy kolme tärkeää rajoitusta:
- Operaattorin paikkaa suoritusjärjestyksessä ei voida muuttaa (esimerkiksi * on ennen +:aa).
- Operaattorin käyttämien operandien lukumäärää ei voi muuttaa ( esim. ++ käyttää yhtä operandia, + käyttää kahta operandia jne ).
- Operaattorifunktioissa ei voi olla oletusarvoparametreja.
Useimpia operaattoreita voidaan kuormittaa, poikkeuksena seuraavat, joita ei siis voi kuormittaa: . : ? .* (dereferenssi pointteri luokan jäseneen). esiprosessorin operaattoreita ei myöskään voi kuormittaa.
On syytä havaita, että C++:ssa operaattorikäsite on hyvin laaja: esimerkiksi [] on indeksioperaattori ja () on funktion kutsuoperaattori. Kaikki muut paitsi sijoitusoperaattori ( = ) periytyvät johdetulle luokalle. Johdettu luokka voi kuitenkin itse kuormittaa operaattoreita mielensä mukaan.
Olemme jo käyttäneet kahta kuormitettua operaattoria, << ja >> operaattoreita konsoli-I/O:n suoritukseen.
Vaikkakin operaattoria kuormitettaessa se voidaan koodata tekemään melkein mitä vain, hyvien tapojen mukaisesti on kuitenkin tapana säilyttää toiminta operaattorin alkuperäisen toiminnan tapaisena ( mitenkäs on << ja >> operaattoreiden kanssa, left ja right shift ??? ).
Binäärioperaattoreiden kuormitus ( 2 operandia )
Kun jäsenoperaattorifunktiolla kuormitetaan binäärioperaattoria ( +, -, /, ....), funktiolle välitetään yksi parametri, jonka arvoksi tulee operaattorin oikealla puolella olevan olion arvo. Esim a + b, b on parametrina ko. funktioon. Operaattorin vasemmalla puolella oleva olio, on se, joka generoi kutsun. Siis operaattorifunktio on sen luokan jäsenfunktio, johon olio a on määritelty.
Operaattorifunktioita voidaan kirjoittaa monella eri tavalla. Tässä esitetyt esimerkit ovat vain joitain tapoja.
Seuraavassa on kuormitettu + operaattoria koord luokassa kahteen otteeseen, siten että lasketaan kaksi koord luokan oliota yhteen ja siten, että koord luokan olioon lisätään kokonaisluku.
#include <iostream> class coord { int x, y; public: coord() { x=0; y=0; } coord( int i, int j ) { x = i; y = j; } void get_xy( int &i, int &j ) { i = x; j = y; } coord operator+( coord ob ); coord operator+( int i ); }; coord coord::operator+( coord ob ) { coord temp; temp.x = x + ob.x; temp.y = y + ob.y; return temp; } coord coord::operator+( int i ) { coord temp; temp.x = x + i; temp.y = y + i; return temp; } main() { coord o1( 10, 7 ), o2( 5, 3 ), o3, o4, o5; int x, y; o3 = o1 + o2; o3.get_xy( x, y ); cout << "(o3=o1+o2) x: " << x << " y: " << y << "\n"; o4 = o1 + 10; o4.get_xy( x, y ); cout << "(o4=o1+10) x: " << x << " y: " << y << "\n"; // o4 = 10 + o1 aiheuttaisi virheen. o5 = o1 + o2 + o3 + o4; o1.get_xy( x, y ); cout << "(o1) x: " << x << " y: " << y << "\n"; o2.get_xy( x, y ); cout << "(o2) x: " << x << " y: " << y << "\n"; o3.get_xy( x, y ); cout << "(o3) x: " << x << " y: " << y << "\n"; o4.get_xy( x, y ); cout << "(o4) x: " << x << " y: " << y << "\n"; o5.get_xy( x, y ); cout << "(o5) x: " << x << " y: " << y << "\n"; (o1 + o2 + o3 + o4 + o5).get_xy( x, y ); cout << "(o1 + o2 + o3 + o4 + o5) x: " << x << " y: " << y << "\n"; return 0; }
Huomaa jos operator+ funktio ei palauttaisi coord-tyyppistä oliota o1 + o2 ei olisi coord-tyyppinen, jolloin sitä ei voisi sijoittaa o3:n arvoksi ( o3 = o1 + o2 ). Nyt myös kokonainen yhteenlaskujen ketju: o5 = o1 + o2 + o3 + o4; on mahdollinen.
Huomaa että myös lauseke (o1 + o2 + o3 + o4 + o5).get_xy( x, y ); on täysin oikein.
Kun kuormitetaan operaattoria siten, että kutsussa oikealla puolella on muu tyyppi kuin vasemmalla puolella ( o4 = o1 + 10 ) on tärkeää havaita, että 'muu' tyyppi on aina oikealla puolella. Esimerkin integerin tapauksessa, jos lauseke olisi ollut 10 + o1, kyseessä olisi kokonaisluku integer-tyypin operaattorin kuormitus, mitä ei olla toteutettu. Pian opitaan kuitenkin tapa kiertää edellinen rajoitus.
KAIKKEIN YLEISIMMIN OPERAATTORIFUNKTIOT PALAUTTAVAT OLION, JOKA ON SAMAA TYYPPIÄ KUIN LUOKKA, JONNE OPERAATTORI ON MÄÄRITELTY, TAVALLISIN POIKKEUS SÄÄNNÖSTÄ OVAT RELAATIO- JA LOOGISET OPERAATTORIT.
Kuormitetaan coord-luokalle - ja = operaattoreita.
#include <iostream> class coord { int x, y; public: coord() { x=0; y=0; } coord( int i, int j ) { x = i; y = j; } void get_xy( int &i, int &j ) { i = x; j = y; } coord operator-( coord ob ); coord operator-( int i ); coord operator=( coord ob ); }; coord coord::operator=( coord ob ) { this->x = ob.x; // tai pelkkä x = ob.x; this->y = ob.y; return *this; } coord coord::operator-( coord ob ) { coord temp; temp.x = x - ob.x; temp.y = y - ob.y; return temp; } coord coord::operator-( int i ) { coord temp; temp.x = x - i; temp.y = y - i; return temp; } main() { coord o1( 10, 7 ), o2( 5, 3 ), o3, o4, o5; int x, y; o3 = o1 - o2; o3.get_xy( x, y ); cout << "(o3=o1-o2) x: " << x << " y: " << y << "\n"; o4 = o1 - 10; o4.get_xy( x, y ); cout << "(o4=o1-10) x: " << x << " y: " << y << "\n"; o5 = o1 - o2 - o3 - o4; o1.get_xy( x, y ); cout << "(o1) x: " << x << " y: " << y << "\n"; o2.get_xy( x, y ); cout << "(o2) x: " << x << " y: " << y << "\n"; o3.get_xy( x, y ); cout << "(o3) x: " << x << " y: " << y << "\n"; o4.get_xy( x, y ); cout << "(o4) x: " << x << " y: " << y << "\n"; o5.get_xy( x, y ); cout << "(o5) x: " << x << " y: " << y << "\n"; (o5 - o1 - o2 - o3 - o4).get_xy( x, y ); cout << "(o5 - o1 - o2 - o3 - o4) x: " << x << " y: " << y << "\n"; o1 = o5; o1.get_xy( x, y ); cout << "(o1) x: " << x << " y: " << y << "\n"; o5.get_xy( x, y ); cout << "(o5) x: " << x << " y: " << y << "\n"; return 0; }
- operaattorin kohdalla, huomaa järjestys: kumpi vähennetään kummasta. Kutsussa oikeanpuoleinen operandi välitetään argumenttina.
Sijoitusoperaattori poikkeaa muista siinä, että siinä vasemman puoleisen operandin arvoa muutetaan ja funktiosta palautetaan kyseinen muutettu operandi *this. Operandia oltaisiin toki voitu muuttaa ja jättää palauttamatta itse operandia mutta tällöin ketjusijoitus o1 = o2 = o3; ei olisi ollut mahdollista.
Kun funktioihin välitettiin olioita tai palautettiin olioita tapahtui epämiellyttäviä väliaikaisten olioiden luonteja, joiden konstruktoreita ei kutsuttu, mutta destruktoreita kutsuttiin. Jotta vältettäisiin kyseisten väliaikaisten olioiden luonti, käytetään usein funktioiden argumentteina referenssejä.
coord coord::operator+( coord &ob2 ) { coord temp; temp.x = x + ob2.x; temp.y = y + ob2.y; return temp; }
Relaatio- ja loogisten operaattoreiden kuormitus
Mikäli säilytetään relaatio- ja loogisten operaatioiden alkuperäinen merkitys, kuormitettaessa kyseisiä operaattoreita operaattorifunktiot palauttavat joko True:n tai False:n.
Kuormitetaan == ja && operaattoreita.
#include <iostream> class coord { int x, y; public: coord() { x=0; y=0; } coord( int i, int j ) { x = i; y = j; } void get_xy( int &i, int &j ) { i = x; j = y; } int operator==( coord ob2 ); int operator&&( coord ob2 ); }; int coord::operator==( coord ob2 ) { if (x == ob2.x && y == ob2.y) return 1; else return 0; } int coord::operator&&( coord ob2 ) { return ((x && ob2.x) && (y && ob2.y)); // tarkoittaa että True palautuu, mikäli kummankaan pisteen x eikä // y ole nollia } main() { coord o1( 1, 1 ), o2( 1, 1 ), o3( 1, 0 ), o4( 0, 1 ); cout << "o1 = 1,1\no2 = 1,1\no3 = 1,0\no4 = 0,1\n"; if (o1 == o2) cout << "o1 = o2\n"; else cout << "o1 <> o2\n"; if (o1 == o3) cout << "o1 = o3\n"; else cout << "o1 <> o3\n"; if (o1 == o4) cout << "o1 = o4\n"; else cout << "o1 <> o4\n"; if (o2 == o3) cout << "o2 = o3\n"; else cout << "o2 <> o3\n"; if (o2 == o4) cout << "o2 = o4\n"; else cout << "o2 <> o4\n"; if (o3 == o4) cout << "o3 = o4\n"; else cout << "o3 <> o4\n"; if (o1 && o2) cout << "o1 && o2 on true\n"; else cout << "o1 && o2 on false\n"; if (o1 && o3) cout << "o1 && o3 on true\n"; else cout << "o1 && o3 on false\n"; if (o1 && o4) cout << "o1 && o4 on true\n"; else cout << "o1 && o4 on false\n"; if (o2 && o3) cout << "o2 && o3 on true\n"; else cout << "o2 && o3 on false\n"; if (o2 && o4) cout << "o2 && o4 on true\n"; else cout << "o2 && o4 on false\n"; if (o3 && o4) cout << "o3 && o4 on true\n"; else cout << "o3 && o4 on false\n"; return 0; }
Unaarioperaattoreiden kuormitus
Unaarioperaattorit tarvitsevat vain yhden operandin esim ++. Jos unaarioperaattoria kuormitetaan käyttäen jäsenfunktiota, kuormitusfunktiolla ei ole lainkaan argumentteja ja olion arvo muuttuu funktiossa sekä palautetaan itse olio *this:n avulla.
HUOMAA! Oletusarvoisesti kutsussa on ensin operaattori, sitten operandi. Esim ++ob tai -ob.
Kuormitetaan coord-luokassa ++ operaattoria:
#include <iostream> class coord { int x, y; public: coord() { x=0; y=0; } coord( int i, int j ) { x = i; y = j; } void get_xy( int &i, int &j ) { i = x; j = y; } coord operator++(); }; coord coord::operator++() { x++; y++; return *this; } main() { coord o1( 1, 1 ); int x, y; o1.get_xy( x, y ); cout << "o1: " << x << ", " << y << "\n"; ++o1; o1.get_xy( x, y ); cout << "o1: " << x << ", " << y << "\n"; o1++; // Warning NONAME00.CPP 27: Overloaded prefix 'operator ++' // used as a postfix operator in function main() o1.get_xy( x, y ); cout << "o1: " << x << ", " << y << "\n"; return 0; }
Kun halutaan erottaa ++ob ja ob++, voidaan käyttää seuraavaa:
coord coord::operator++() // ++ob: muutetaan ensin, sitten vasta käytetään { x++; y++; return *this; } coord coord::operator++(int z) // ob++: käytetään ensin, sitten vasta muutetaan { // z:n arvona on 0 coord temp; temp = *this; x++; y++; return temp; }
Tällöin edellisen pääohjelman lopussa seuraavat kutsut tulostavat kommenteissa olevat arvot: ( o1:n arvona on alussa 3,3 )
o2 = ++o1; o1.get_xy( x, y ); cout << "o1: " << x << ", " << y << "\n"; // tulostaa 4,4 o2.get_xy( x, y ); cout << "o2: " << x << ", " << y << "\n"; // tulostaa 4,4 o2 = o1++; o1.get_xy( x, y ); cout << "o1: " << x << ", " << y << "\n"; // tulostaa 5,5 o2.get_xy( x, y ); cout << "o2: " << x << ", " << y << "\n"; // tulostaa 4,4
Kuormitetaan etumerkki-operaattoria ja tavallista vähennyslaskua:
#include <iostream> class coord { int x, y; public: coord() { x=0; y=0; } coord( int i, int j ) { x = i; y = j; } void get_xy( int &i, int &j ) { i = x; j = y; } coord operator-(coord ob); // tavallinen vähennyslasku coord operator-(); // etumerkki }; coord coord::operator-() // etumerkki: -ob { coord temp; temp.x = -x; temp.y = -y; return temp; } coord coord::operator-(coord ob) // vähennyslasku: *this - ob { coord temp; temp.x = x - ob.x; temp.y = y - ob.y; return temp; } main() { coord o1( 1, 1 ), o2( -2, -2 ), o3; int x, y; o1.get_xy( x, y ); cout << "o1: " << x << ", " << y << "\n"; // tulostaa 1,1 o2.get_xy( x, y ); cout << "o2: " << x << ", " << y << "\n"; // tulostaa -2,-2 o3 = o1 - o2; o3.get_xy( x, y ); cout << "o3 = o1 - o2: " << x << ", " << y << "\n"; // tulostaa 3,3 o3 = -o1; o3.get_xy( x, y ); cout << "o3 = -o1: " << x << ", " << y << "\n"; // tulostaa -1,-1 o1.get_xy( x, y ); cout << "o1: " << x << ", " << y << "\n"; // tulostaa 1,1 return 0; }
Friend-operaattorifunktiot
Tiettyyn luokkaan liittyvää operaattoria voidaan kuormittaa paitsi jäsenfunktion avulla, myös friend-funktion avulla. Friend-funktioilla ei ole this-pointteria. Binäärioperaattoreiden tapauksessa molemmat operandit välitetään friend operaattorifunktiolle argumentteina. Unaarioperaatioissa ainoa operandi välitetään myös argumenttina. Friendin käyttö jäsenfunktion sijasta ei ole sen parempi tai huonompi ratkaisu lukuunottamatta yhtä poikkeusta, josta puhutaan hetken kuluttua.
HUOM: friend-funktiota ei voida käyttää sijoitusoperaation kuormitukseen.
coord-luokan operator+() kuormitettu friendin avulla.
#include <iostream> class coord { int x, y; public: coord() { x=0; y=0; } coord( int i, int j ) { x = i; y = j; } void get_xy( int &i, int &j ) { i = x; j = y; } friend coord operator+( coord ob1, coord ob2 ); }; coord operator+( coord ob1, coord ob2 ) // ob1 + ob2 tässä järjestyksessä { coord temp; temp.x = ob1.x + ob2.x; temp.y = ob1.y + ob2.y; return temp; } main() { coord o1( 10, 7 ), o2( 5, 3 ), o3; int x, y; o3 = o1 + o2; o3.get_xy( x, y ); cout << "(o3=o1+o2) x: " << x << " y: " << y << "\n"; return 0; }
Kuormitettaessa friend:n avulla operaattoreita pystytään toteuttamaan eräs hyvin tärkeä ominaisuus, jota jäsenfunktion avulla ei voida toteuttaa. Nimittäin ensimmäisenä operandina voi olla joku muu tyyppi kuin kyseisen luokan tyyppi. Esim. ob1 = 10 + ob2. Luokan jäsenfunktioiden avulla kyseinen yhteenlasku voitaisiin suorittaa vain toisessa järjestyksessä:
- ob1 = ob2 + 10;
- ob = ob1 + int
- ob = int + ob1
#include <iostream> class coord { int x, y; public: coord() { x=0; y=0; } coord( int i, int j ) { x = i; y = j; } void get_xy( int &i, int &j ) { i = x; j = y; } friend coord operator+( coord ob1, int z ); friend coord operator+( int z, coord ob1 ); }; coord operator+( coord ob1, int z ) // ob1 + z tässä järjestyksessä { coord temp; temp.x = ob1.x + z; temp.y = ob1.y + z; return temp; } coord operator+( int z, coord ob1 ) // z + ob1 tässä järjestyksessä { return (operator+(ob1, z)); // tai kirjoitetaan yo. funktio uudelleen } main() { coord o1( 10, 7 ), o3; int x, y; o3 = o1 + 5; o3.get_xy( x, y ); cout << "(o3=o1+5) x: " << x << " y: " << y << "\n"; o3 = 5 + o1; o3.get_xy( x, y ); cout << "(o3=5+o1) x: " << x << " y: " << y << "\n"; return 0; }
Koska friend-funktioilla ei ole this-pointteria, joudutaan sellaisissa tapauksissa, joissa olion arvoa muutetaan, esim ++ tai --, käyttämään argumentissa referenssiä.
#include <iostream> class coord { int x, y; public: coord() { x=0; y=0; } coord( int i, int j ) { x = i; y = j; } void get_xy( int &i, int &j ) { i = x; j = y; } friend coord operator++( coord &ob ); friend coord operator++( coord &ob, int z ); }; coord operator++( coord &ob1 ) // ++ob1 { ob1.x++; ob1.y++; return ob1; } coord operator++( coord &ob1, int z ) // ob1++ { coord temp; temp = ob1; ob1.x++; ob1.y++; return temp; } main() { coord o1( 10, 7 ), o3; int x, y; o3 = o1++; o3.get_xy( x, y ); cout << "o3 = o1++ x: " << x << " y: " << y << "\n"; o1.get_xy( x, y ); cout << "o1 x: " << x << " y: " << y << "\n"; o3 = ++o1; o3.get_xy( x, y ); cout << "o3 = ++o1 x: " << x << " y: " << y << "\n"; o1.get_xy( x, y ); cout << "o1 x: " << x << " y: " << y << "\n"; return 0; }
Sijoitusoperaattori
Mikäli olion sijoituksessa ei tarvitse tehdä muuta kuin kopioida data-arvot toisiinsa sellaisinaan kuten koord-luokassa esimerkiksi, ei ole mitään tarvetta tehdä omaa sijoitus-operaattorifunktiota. Jos kuitenkin tilanne on esimerkiksi sellainen kuin dynaamisessa pinossa tai merkkijono-luokassa, joissa luokan dataosassa on osoitin ja muistia on varattu dynaamisesti kyseiseen osoittimeen, normaali dataosien kopiointi ei riitä, on varattava oikean kokoinen puskuri ja siirrettävä data kyseisten dynaamisten puskureiden välillä. Ongelmia kuitenkin tuottaa edelleen kielen ominaisuus kutsua destruktoria muttei konstruktoria väliaikaisten muuttujien tapauksessa (argumentit ja palautusarvot).
Tehtävässä, jossa kuormiteltiin operaattoreita mjono-tyypille saatoit ratkaista sijoitusoperaation siten, että käytit copy-konstruktoria, jolloin seuraava oli mahdollista:
mjono mjono::operator=( mjono z ) { if (len != 0) { printf("Vapautetaan puskuri osoitteesta %x\n", p ); free( p ); } len = z.len; if (len != 0) { p = (char *) malloc( len + 1 ); if ( !p ) { cout << "Varausvirhe\n"; exit( 1 ); } printf( "Varattu %d tavua osoitteesta %x\n", len+1, p ); strcpy( p, z.p ); } return *this; }
Jos copy-konstruktoria ei kuitenkaan käytettäisi, sijoitusoperaatio voitaisiin toteuttaa referenssien avulla:
#include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> class mjono{ char *p; int len; public: mjono( char *ptr ); ~mjono(); char *get() { return p; } mjono &operator=(mjono &ob); }; mjono::mjono( char *ptr ) { len = strlen( ptr ); p = new char [ len + 1 ]; if ( !p ) { cout << "Varausvirhe\n"; exit( 1 ); } printf( "Varattu %d tavua osoitteesta %x\n", len+1, p ); strcpy( p, ptr ); } mjono::~mjono() { if (len != 0) { printf("Vapautetaan puskuri osoitteesta %x\n", p ); delete p ; } } mjono &mjono::operator=( mjono &ob ) { if (len != 0) { printf("Vapautetaan puskuri osoitteesta %x\n", p ); delete p; } len = ob.len; if (len != 0) { p = new char [len + 1]; if ( !p ) { cout << "Varausvirhe\n"; exit( 1 ); } printf( "Varattu %d tavua osoitteesta %x\n", len+1, p ); strcpy( p, ob.p ); } return *this; } main() { mjono mj1("Terve"), mj2("vaan"); cout << mj1.get() << '\n'; cout << mj2.get() << '\n'; mj2 = mj1; cout << mj1.get() << '\n'; cout << mj2.get() << '\n'; return 0; }
operator[ ]
Taulukon alkioiden käsittely tapahtuu [ ] -operaattorin avulla. Esim mj1[ 1 ] = 'e'; tai a = mj1[ 5 ]; Indeksi-operaattorin voi sijaita lausekkeessa siten, että sen avulla taulukkoon laitetaan arvo tai taulukosta luetaan arvo, jonka takia operaattorifunktion on palautettava referenssi. Operaattorifunktio on binäärinen, argumenttina indeksin arvo.
Lisätään edelliseen esimerkkiin operator[]-funktio.
#include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> class mjono{ char *p; int len; public: mjono( char *ptr ); ~mjono(); char *get() { return p; } mjono &operator=(mjono &ob); char &operator[]( int index ); }; mjono::mjono( char *ptr ) { len = strlen( ptr ); p = new char [ len + 1 ]; if ( !p ) { cout << "Varausvirhe\n"; exit( 1 ); } printf( "Varattu %d tavua osoitteesta %x\n", len+1, p ); strcpy( p, ptr ); } mjono::~mjono() { if (len != 0) { printf("Vapautetaan puskuri osoitteesta %x\n", p ); delete p ; } } mjono &mjono::operator=( mjono &ob ) { if (len != 0) { printf("Vapautetaan puskuri osoitteesta %x\n", p ); delete p; } len = ob.len; if (len != 0) { p = new char [len + 1]; if ( !p ) { cout << "Varausvirhe\n"; exit( 1 ); } printf( "Varattu %d tavua osoitteesta %x\n", len+1, p ); strcpy( p, ob.p ); } return *this; } char &mjono::operator[]( int index ) { if (index < 0 || index > len) { cout << "raja-arvo virhe\n"; exit(1); } return p[index]; } main() { mjono mj1("Terve"), mj2("vaan"); int i; for (i=0; i < 5; i++) cout << mj1[i]; cout << "\n"; i=0; while (mj2[i] != '\0') { mj2[i] = mj1[i]; i++; } for (i=0; i < 4; i++) cout << mj2[i]; cout << "\n"; mj1[7] ='a'; // raja-arvo virhe return 0; }
Kopio lisenssistä (englanniksi) löytyy täältä.
Alkuperäinen (c) Petteri Hämäläinen
