Cpp IO järjestelmän perusteet

Mureakuha

Loikkaa: valikkoon, hakuun

C++ tyylistä I/O:ta on jo käytetty kurssin alusta lähtien, mutta nyt on aika selvittää, mistä itse asiassa on kysymys. Kuten C++:n edeltäjä C, myös C++ sisältää joustavan ja tehokkaan I/O järjestelmän. C++ tukee edelleen kaikkia C:n I/O-järjestelmän toimintoja. C++:ssa on lisäksi täydellinen joukko oliopohjaisia I/O-rutiineita.

Suurin hyöty C++:n I/O-järjestelmästä tulee siitä, että sitä voidaan kuormittaa omiin luokkiin eli I/O-järjestelmän piiriin voidaan tuoda uusia tyyppejä.

Kuten C:ssä myös C++:ssa konsoli-I/O ja tiedosto-I/O eivät poikkea juurikaan toisistaan. Kyseessä on oikeastaan kaksi näkökulmaa samaan mekanismiin.

Sisällysluettelo

C++:n I/O perusteita

Kuten C:n I/O-järjestelmä, myös C++:n I/O-järjestelmä toimii 'virtojen' (streams) avulla. Stream on looginen laite, joka joko tuottaa informaatiota tai käyttää informaatiota. Stream on kytketty fyysiseen I/O-laitteeseen C++:n I/O-järjestelmän kautta. Kaikki streamit käyttäytyvät samalla tavalla riippumatta siitä, mihin fyysiseen laitteeseen ne on kytketty. Tästä seuraa, että I/O-järjestelmä toimii periaatteessa minkä tahansa fyysisen laitteen kanssa. Samaa metodia voidaan käyttää tulostettiinpa näytölle, levylle tai printterille.

Kuten tietänet, kun C-ohjelma käynnistyy, avataan automaattisesti 3 etukäteen määriteltyä streamia: stdin, stdout ja stderr. Vastaavalla tavalla C++ ohjelman käynnistyksessä avataan automaattisesti 4 streamia:

streamseliteoletuslaite
cinstandardi syöttövirtanäppäimistö
coutstandardi tulostusvirtanäyttö
cerrstandardi virhetulostusvirtanäyttö
clogcerr:n puskuroitu versionäyttö

Kuten C:ssäkin virrat voidaan ohjata tarvittaessa toisille laitteille, mikäli järjestelmä tukee I/O:n uudelleenohjausta.

C++:n I/O-tuki löytyy header-tiedostosta iostream. Tässä tiedostossa on määriteltynä luokkahierarkia, joka tukee I/O-operaatioita. Alimman tason I/O-luokka on nimeltään streambuf. Luokka sisältää perus-input ja -output operaatiot ja sitä käytetään lähinnä muiden luokkien kantaluokkana. Ellet aio tehdä edistyksellisimpiä I/O-ohjelmia, et tule tarvitsemaan streambuf luokkaa suoraan. Hierarkian seuraavana luokkana on ios. Tässä luokassa on toteutettuna stream-I/O:hon liittyvä formatointi, virheen tarkistus sekä statusinformaatio. ios on kantaluokkana seuraaville luokille: istream, ostream ja iostream. Näiden avulla tapahtuu syöttö, tulostus ja syöttö/tulostus.

ios-luokka sisältää useita jäsenfunktioita ja muuttujia, jotka ohjaavat stream:n perustoimintaa. Kun ohjelmaasi on sisällytetty iostream, ohjelmastasi on pääsy ios-luokkaan.

Formatoitu I/O

Tähän mennessä kaikki tulostus on tapahtunut standardi formaatilla. Kuitenkin tulostusta voidaan formatoida saman tapaisesti kuin C:n printf-funktiossa. Syötteiden muotoja voidaan myös muutella.

Jokaiseen C++:n streamiin liittyy joukko formatointilippuja, jotka määräävät tulostuksen muodon. Liput on koodattu long integer tyyppiseen muuttujaan. Seuraava ios luokassa määritelty muuttuja määrittelee nimivakiot lipuille:

// ios format flags
enum {
  skipws    = 0x0001, // skip whitespace on input
  left      = 0x0002, // left-adjust output
  right     = 0x0004, // right-adjust output
  internal  = 0x0008, // padding after sign or base indicator
  dec   = 0x0010,     // decimal conversion
  oct   = 0x0020,     // octal conversion
  hex   = 0x0040,     // hexadecimal conversion
  showbase  = 0x0080, // use base indicator on output
  showpoint = 0x0100, // force decimal point (floating output)
  uppercase = 0x0200, // upper-case hex output
  showpos   = 0x0400, // add '+' to positive integers
  scientific= 0x0800, // use 1.2345E2 floating notation
  fixed     = 0x1000, // use 123.45 floating notation
  unitbuf   = 0x2000, // flush all streams after insertion
  stdio     = 0x4000  // flush stdout, stderr after insertion
};

Kun formatointi lippu asetetaan, ominaisuus kytkeytyy päälle. Kun lippu poistetaan, oletusformaatti astuu voimaan. Seuraavassa selitykset lipuille:

skipws Kun lippu on asetettu, syötteen alussa olevat white space merkit ohitetaan (tyhjä, tabulaatio, enter). Kun lippu poistetaan, white space merkkejä ei ohiteta. Käytännössä lippuun tarvitsee koskea vain, kun luetaan tietyn tyyppisiä levytiedostoja.

left, right, internal Kun left-lippu on asetettu, tulostus tasataan vasemmalle. Kun right-lippu on asetettu, tulostus tasataan oikealle. Kun internal-lippu on asetettu, numeerinen arvo tulostetaan siten, että se täyttää koko kentän (kentän leveydestä kohta) sekä etumerkin ja luvun väli täytetään tyhjillä merkeillä. Jos mikään lipuista ei ole asetettuna, tulostus on oletusarvoisesti tasattu oikealle.

dec, oct, hex Oletusarvoisesti kokonaislukujen tulostus tapahtuu desimaalisena. Se voidaan kuitenkin muuttaa oktaaliseksi tai heksadesimaaliseksi. Kun tulostus halutaan palauttaa desimaaliseksi, asetetaan dec-lippu.

showbase Kun lippu asetetaan, numeeristen arvojen kantaluku näytetään. Esimerkiksi heksadesimaalinen luku 1F tulostettaisiin: 0x1F.

showpoint Kun lippu asetetaan, desimaalipiste ja loppunollat näytetään kaikille liukuluvuille. Esimerkiksi liukuluku 5 olisi 5.000000.

uppercase Kun lippu asetetaan, heksadesimaaliesityksen 'x' ja eksponenttilukujen 'e' näytetään isoilla kirjaimilla.

showpos Kun lippu asetetaan, positiivisille desimaaliluvuille näytetään etumerkki (+).

scientific, fixed Kun scientific-lippu asetetaan liukuluvut näytetään tieteellisellä notaatiolla. Kun fixed-lippu asetetaan, luvut näytetään normaalisti. Oletusarvoisesti fixed on asetettu ja luvut näytetään 6 desimaalilla. Kun kumpikaan lipuista ei ole asetettu, kääntäjä valitsee sopivimman.

unitbuf Kun lippu asetetaan, C++ I/O-järjestelmän suorituskyky paranee joillain kääntäjillä.

stdio Kun lippu asetetaan, jokainen stream tyhjennetään (flush) jokaisen tulostuksen jälkeen eli käytännössä tulostus menee fyysiselle laitteelle asti.

Formaattilipun asetukseen käytetään setf()-funktiota. Funktio on ios-luokan jäsenfunktio ja sen yleisimmin käytetty muoto on:

long setf( long flags );

Funktio palauttaa lippujen vanhan arvon ja asettaa argumentissa määritellyt liput (muut liput jätetään koskematta). Esimerkiksi, jos halutaan asettaa showpos-lippu, asia voidaan hoitaa esimerkiksi seuraavasti:

stream.setf( ios::showpos ); // stream on se virta, jota muutetaan 
 

setf() on ios-luokan jäsenfunktio, jolloin se vaikuttaa sellaisiin virtoihin, jotka on luotu ios-luokkaan. Jokainen setf:n kutsu on tehtävä tietyn stream:n suhteen, pelkkää setf-funktiota ei voi mitenkään kutsua.

Yhdellä setf:n kutsulla voidaan asettaa useita lippuja. Esimerkiksi asetetaan cout:n liput showbase ja hex:

cout.setf( ios::showbase | ios::hex );

Koska formaattiliput on määritelty ios-luokassa, lippuihin pääsee käsiksi vain seuraavasti: ios::lippu.

Setf:n komplementti on unsetf. Tällä ios-luokan jäsenfunktiolla nollataan yksi tai useampi lippu. Sen yleisimmin käytetty muoto on:

long unset( long flags );

Funktio palauttaa lippujen vanhan arvon ja poistaa asetuksen argumentissa määritellyiltä lipuilta (muut liput jätetään koskematta).

Kun halutaan ainoastaan tietää tämän hetken lipputilanne, muttei haluta tehdä muutoksia, käytetään ios:n jäsenfunktiota flags, joka palauttaa lippujen arvon:

long flags();

flags-funktiolla on toinenkin muoto, jolla voidaan asettaa liput annettuun arvoon:

long flags( long f );

Argumentin f bittikuvio kopioidaan muuttujaan, joka ylläpitää lippuinformaatiota eli kaikki liput muutetaan annettuihin arvoihin riippumatta vanhasta arvosta. Funktio palauttaa vanhan lipputilanteen.

setf-esimerkki:

#include <iostream>
 
main()
{
  // tulostus oletusarvoilla
  cout << 123.23 << " hello " << 100 << '\n';
  cout << 10 << ' ' << -10 << '\n';
  cout << 100.0 << "\n\n";
 
  // muutetaan formaattia
  cout.setf( ios::hex | ios::scientific );
  cout << 123.23 << " hello " << 100 << '\n';
 
  cout.setf( ios::showpos );
  cout << 10 << ' ' << -10 << '\n';
 
  cout.setf( ios::showpoint || ios::fixed );
  cout << 100.0 << '\n';
 
  return 0;
}

Tulostuu:

123.23 hello 100
10 -10
100
 
1.232300e+02 hello 64
a fffffff6  // showpos vaikuttaa vain desimaaliseen tulostukseen
+1.000000e+02

unsetf-esimerkki:

#include <iostream>
 
main()
{
  // muutetaan formaattia
  cout.setf( ios::hex | ios::uppercase | ios::showbase );
  cout << 88 << '\n';
 
  cout.unsetf( ios::uppercase );
  cout << 88 << '\n';
 
  return 0;
}

Tulostuu:

0X58
0x58

flags-esimerkki. Kiinnitä erityisesti huomiota showflags funktioon, jota voit käyttää hyväksesti jatkossa.

#include <iostream>
 
void showflags();
 
main()
{
  // oletusarvot
  showflags();
 
  // muutetaan formaattia
  cout.setf( ios::oct | ios::fixed | ios::showbase );
  showflags();
 
  long f = 0x04A4;
  cout.flags( f );
  showflags();
 
  return 0;
}
 
void showflags()
{
  long   i, f;
  int   j;
 
  char flgs[15][12] = {
      "skipws",
      "left",
      "right",
      "internal",
      "dec",
      "oct",
      "hex",
      "showbase",
      "showpoint",
      "uppercase",
      "showpos",
      "scientific",
      "fixed",
      "unitbuf",
      "stdio"
  };
 
  f = cout.flags();  // haetaan asetus
 
  // tarkistetaan jokainen lippu
  for (i=1, j=0; i <= 0x4000; i = i << 1, j++)
    if ( i&f ) cout << flgs[j] << " is on\n";
    else cout << flgs[j] << " is off\n";
  cout << "\n";
}

Tulostuu:

skipws is on
left is off
right is off
internal is off
dec is off
oct is off
hex is off
showbase is off
showpoint is off
uppercase is off
showpos is off
scientific is off
fixed is off
unitbuf is on
stdio is off
 
skipws is on
left is off
right is off
internal is off
dec is off
oct is on
hex is off
showbase is on
showpoint is off
uppercase is off
showpos is off
scientific is off
fixed is on
unitbuf is on
stdio is off
 
skipws is off
left is off
right is on
internal is off
dec is off
oct is on
hex is off
showbase is on
showpoint is off
uppercase is off
showpos is on
scientific is off
fixed is off
unitbuf is off
stdio is off

width(), precision() ja fill()

ios:ssa on 3 jäsenfunktiota, joilla tulostusta formatoidaan: width(), precision() ja fill().

Oletusarvoisesti tulostettava arvo vie näytöltä tms. vain sen verran tilaa, kuin tarvitaan arvon esittämiseen. Minimikentän leveys voidaan määrittää käyttämällä width()-funktiota:

int width( int w );

Funktion argumenttina on kentän uusi leveys ja funktio palauttaa vanhan leveyden. Jotkut toteutukset vaativat, että kentän leveys on määriteltävä uudelleen ennen jokaista tulostuslausetta, muuten se palautuu takaisin oletusarvoon. Jos tulostus vie vähemmän tilaa kuin width-funktiossa on määritelty, kenttä täytetään täyttömerkillä, joka on oletusarvoisesti tyhjä merkki.

Desimaaliluvusta näytetään oletusarvoisesti 6 desimaalia. Desimaalien lukumäärää voidaan muuttaa precision()-funktiolla:

int precision( int p );

Uusi arvo viedään p:ssä ja vanha arvo palautetaan.

HUOMAA: Borland C++ 4.jotain muuttaa precision funktiolla kaikkien näytettävien numeroiden lukumäärää. Katso esimerkki perässä. Jotta tarkkuus muuttuu oikein on käytettävä lisäksi:

cout.setf( ios::fixed );

Kenttä täytetään täyttö merkillä, joka voidaan määrätä funktiolla:

char fill( char ch );

Kutsun jälkeen uusi täyttö merkki on ch, ja vanha merkki palautetaan.

#include <iostream>
 
main()
{
  cout << 123.234567 << '\n';
  cout << "hello" << '\n';
 
  cout.width( 10 );
  cout << "hello" << '\n';
 
  cout.fill('%');
  cout.width( 10 );
  cout << "hello" << '\n';
 
  cout.setf( ios::left );
  cout.width( 10 );
  cout << "hello" << '\n';
 
  cout.width( 10 );
  cout.precision(3);
  cout << 123.234567 << '\n';
 
  cout.width( 10 );
  cout.precision(6);
  cout << 123.234567 << '\n';
 
  cout.width( 10 );
  cout.precision(9);
  cout << 123.234567 << '\n';
 
  return 0;
}

Tulostuu:

Borland:    Kirjan mukaan:
123.235     123.234567
hello       hello
     hello       hello
%%%%%hello  %%%%%hello
hello%%%%%  hello%%%%%
123%%%%%%%  123.234%%%
123.235%%%  123.234567
123.234567  123.234567

Tehdään taulukkomuotoinen tulostus:

#include <iostream>
#include <math.h>
 
main()
{
  double x;
 
  cout.precision(4);
  cout.setf( ios::right );
  cout.width( 9 );
  cout << "x";
  cout.setf( ios::right );
  cout.width( 9 );
  cout << "sqrt(x)";
  cout.setf( ios::right );
  cout.width( 9 );
  cout << "x^2";
  cout << "\n\n";
 
  for (x = 2.0; x <= 10.0; x++ ) {
    cout.width(9);
    cout << x;
    cout.width(9);
    cout << sqrt(x);
    cout.width(9);
    cout << x*x << '\n';
  }
  return 0;
}

Tulostuu:

x  sqrt(x)      x^2
 
  2    1.414        4
  3    1.732        9
  4        2       16
  5    2.236       25
  6    2.449       36
  7    2.646       49
  8    2.828       64
  9        3       81
 10    3.162      100

I/O manipulaattorit

Informaatiota voidaan formatoida toisellakin tavalla kuin edellä esitetyllä. Voidaan käyttää erityisiä funktioita ns. I/O-manipulaattoreita, jotka ovat useimmissa tapauksissa helpompia käyttää kuin ios-formaattiliput ja ios-funktiot.

I/O-manipulaattorit ovat erityisiä I/O-formaatti funktioita, joita käytetään I/O-lausekkeen sisällä eikä erillisinä kuten ios:n jäsenfunktioita. Seuraavassa taulukossa on lueteltu standardimanipulaattorit. Kuten huomannet useat manipulaattorit suorittavat saman asian kuin ios-jäsenfunktiot.

C++ I/O-manipulaattorit
ManipulaattoriI/OTarkoitus
decOnumeerinen data desimaalisena
endlOrivinsiirtomerkki ja streamin tyhjennys
endsOnullin tulostus
flushOstreamin tyhjennys
hexOnumeriinen data hexadesimaalisena
octOnumeerinen data oktaalisena
resetiosflags( long f )I ja Of:ssä ilmoitetut liput pois päältä
setbase( int base )Okantaluku base:ksi
setfill( int ch )Otäyttömerkki ch:ksi
setiosflags( long f )I ja Of:ssä ilmoitetut liput päälle
setprecision( int p )Odesimaalipisteen jälkeen tulostettavien numeroiden lukumäärä p:ksi
setw( int w )Otulostuskentän leveys w:ksi
wsIohitetaan alun whitespace-merkit

HUOMAA: Borland C++ 4.jotain muuttaa setprecision funktiolla kaikkien näytettävien numeroiden lukumäärää. Katso esimerkki perässä.

#include <iostream>
#include <iomanip>
#include <math.h>
 
main()
{
  double x;
 
  cout << hex << 100 << endl;
  cout << oct << 10 << endl;
  cout << setfill('X') << setw(10) << 100 << " hi " << endl;
 
  cout << setfill(' ');
  cout.setf( ios::right );
  cout << setprecision(3);
  cout << setw( 9 ) << "x";
  cout << setw( 9 ) << "sqrt(x)";
  cout << setw( 9 ) << "x^2" << endl << endl;
 
  for (x = 2.0; x <= 10.0; x++ ) {
    cout << setw(9) << x;
    cout << setw(9) << sqrt(x);
    cout << setw(9) << x*x << '\n';
  }
  return 0;
}

Tulostuu:

64
12
XXXXXXX144 hi
  x  sqrt(x)      x^2
 
  2     1.41        4
  3     1.73        9
  4        2       16
  5     2.24       25
  6     2.45       36
  7     2.65       49
  8     2.83       64
  9        3       81
 10     3.16      100

Omat insertterit

Kuten aiemmin mainittu, I/O-operaattoreita voidaan kuormittaa omiin luokkiin. Nyt tarkastellaan, miten << tulostus-operaattorin kuormitus tapahtuu.

C++:ssa tulostusoperaatiota kutsutaan insertioksi ja tulostusoperaattoria << kutsutaan insertio-operaattoriksi. Kun <<-operaattoria kuormitetaan, luodaan insertterifunktio eli lyhyesti insertteri.

Kaikilla insertterifunktioilla on yleinen muoto:

ostream &operator<<( ostream &stream, class_name  ob )
{
  // insertterin runko
  return stream;
}

Ensimmäinen parametri on referenssi ostream-tyyppiseen olioon. Eli streamin on oltava tulostus-stream. (ostream on määritelty ios-luokassa). Toinen parametri on tulostettava olio. Insertteri-funktio palauttaa referenssin ostream-tyyppiseen olioon, jolloin << operaatiota voidaan käyttää monimutkaisemmissakin lausekkeissa kuten:

cout << ob1 << ob2 << ob3;

Itse insertteri-funktiossa voidaan suorittaa mikä tahansa toimenpide, kuitenkin hyvän ohjelmointitavan mukaisesti on syytä suorittaa tulostus ostream:iin.

Aluksi saattaa tuntua yllättävältä, että insertteri ei voi olla sen luokan jäsenfunktio, johon se on suunniteltu. Kun mikä tahansa operaattorifunktio on luokan jäsenfunktio, vasemmanpuoleinen operandi, joka välittyy funktioon epäsuorasti this-pointterin avulla, on olio, joka generoi operaattorifunktion kutsun. Vasemman operandin on siis oltava kyseisen luokan olio. Insertterin tapauksessa vasen operandi on stream ja oikean puoleinen operandi on tulostettava olio. Siis insertteri ei voi olla sen luokan jäsenfunktio, johon tulostettava olio kuuluu. Insertteri on kyseisen luokan friend-funktio. (Se voisi olla myös public-funktio, mutta silloin kadotettaisiin olio-ohjelmoinnin perusperiaatteita: kapselointi.)

#include <iostream>
 
class coord {
  int x, y;
public:
  coord() { x = 0; y = 0; }
  coord( int i, int j ) { x = i; y = j; }
  friend ostream &operator<<( ostream &stream, coord ob );
};
 
ostream &operator<<( ostream &stream, coord ob )
{
  stream << ob.x << ", " << ob.y << '\n';
  return stream;
}
 
main()
{
  coord a, b(1,1), c(10,23);
 
  cout << a << b << c;
  return 0;
}

Tulostuu:

0, 0
1, 1
10, 23

Insertterit on syytä tehdä niin yleisiksi kuin mahdollista. Edellä tulostetaan coord-luokan olion x- ja y-koordinaatit streamiin, riippumatta siitä, mikä stream itseasiassa on. Usein aloitteleva ohjelmoija toteuttaa edellisen insertterin seuraavasti:

ostream &operator<<( ostream &stream, coord ob )
{
  // EI NÄIN
  cout << ob.x << ", " << ob.y << '\n';
  return stream;
}

Nyt insertteriä ei voi käyttää muille streameille kuin cout:lle.

Seuraavassa esimerkissä ei ole käytetty friendiä, jolloin coord-luokan x ja y on jouduttu tekemään public-tyyppisiksi, jotta insertteri funktio voisi niitä käsitellä. Älä kuitenkaan käytä tätä public tapaa ohjelmissasi yleisesti:

#include <iostream>
 
class coord {
public:
  int x, y;
  coord() { x = 0; y = 0; }
  coord( int i, int j ) { x = i; y = j; }
};
 
ostream &operator<<( ostream &stream, coord ob )
{
  stream << ob.x << ", " << ob.y << '\n';
  return stream;
}
 
main()
{
  coord a, b(1,1), c(10,23);
 
  cout << a << b << c;
  return 0;
}

Seuraavassa esimerkissä tulostetaan kolmioita.

#include <iostream>
 
class triangle {
  int height, base;
public:
  triangle( int x ) { height = x; base = x; }
  friend ostream &operator<<( ostream &stream, triangle ob );
};
 
ostream &operator<<( ostream &stream, triangle ob )
{
  int i, j, h, k;
 
  i = j = ob.base - 1;
  for ( h = ob.height - 1; h>0; h--) {
    for ( k = i; k>0; k-- )
      stream << ' ';
    stream << '*';
 
    if ( j!=i ) {
      for ( k = j - i - 1; k>0; k-- )
        stream << ' ';
      stream << '*';
    }
 
    i--;
    stream << '\n';
  }
  for ( k = 0; k < ob.base; k++ )
    stream << '*';
  stream << '\n';
 
  return stream;
}
 
main()
{
  triangle a(5), b(7), c(12);
 
  cout << a << endl << b << endl << c;
  return 0;
}

Omat extraktorit

Vastaavasti kuin << tulostusoperaattoria kuormitetaan, voidaan kuormittaa >> syöttöoperaattoria. C++:ssa syöttösoperaattoria >> kutsutaan extraktio-operaattoriksi ja sitä kuormittavaa funktiota kutsutaan extraktoriksi.

Extraktorin yleinen muoto:

istream &operator>>( istream &stream, class-name &ob )
{
  // extraktorin runko
  return stream;
}

Extraktorit palauttavat referenssin istream-tyyppiseen olioon, eli syöttovirtaan. Ensimmäinen parametri on referenssi syöttövirtaan ja toinen parametri on referenssi olioon, joka saa syötteen. Kuten insertterit, extraktoritkaan eivät voi olla luokan jäsenfunktioita.

Ohjelmassa käytetään coord-luokan extraktoria.

#include <iostream>
 
class coord {
  int x, y;
public:
  coord() { x = 0; y = 0; }
  coord( int i, int j ) { x = i; y = j; }
  friend ostream &operator<<( ostream &stream, coord ob );
  friend istream &operator>>( istream &stream, coord &ob );
};
 
ostream &operator<<( ostream &stream, coord ob )
{
  stream << ob.x << ", " << ob.y << '\n';
  return stream;
}
 
istream &operator>>( istream &stream, coord &ob )
{
  cout << "\nEnter coordinates ( x y ):";
  stream >> ob.x >> ob.y;
  return stream;
}
 
main()
{
  coord a, b(1,1), c(10,23);
 
  cout << a << b << c;
 
  cin >> a;
  cout << a;
 
  return 0;
}

Seuraavassa esimerkissä on "varasto-luokka", joka tallettaa tavaran nimen, lukumäärän varastossa ja yksikköhinnan.

#include <iostream>
#include <string.h>
 
class inventory {
  char     item[40];
  int    onhand;
  double  cost;
public:
  inventory ( char *i, int o, double c ){
    strcpy( item, i );
    onhand = o;
    cost = c;
  }
  friend ostream &operator<<( ostream &stream, inventory ob );
  friend istream &operator>>( istream &stream, inventory &ob );
};
 
ostream &operator<<( ostream &stream, inventory ob )
{
  stream << ob.item << ": " << ob.onhand;
  stream << " on hand at $" << ob.cost << '\n';
  return stream;
}
 
istream &operator>>( istream &stream, inventory &ob )
{
  cout << "\nEnter item name: ";
  stream >> ob.item;
  cout << "Enter Number on hand: ";
  stream >> ob.onhand;
  cout << "Enter cost: ";
  cin >> ob.cost;
  return stream;
}
 
main()
{
  inventory ob("hammer", 4, 12.55);
 
  cout << ob;
 
  cin >> ob;
  cout << ob;
 
  return 0;
}
Tämän dokumentin kopiointi, levittäminen sekä muokkaaminen on sallittua GNU Free Documentation Licensen version 1.2 tai uudemman Free Software Foundationin julkaiseman version mukaisesti, ilman muuttumattomuuslauseketta tai kansitekstejä. Tätä koskee vastuuvapaus.
Kopio lisenssistä (englanniksi) löytyy täältä.


Alkuperäinen (c) Petteri Hämäläinen

Henkilökohtaiset työkalut