Ylös Edellinen Seuraava Otsikkosivu Hakemisto Sisällys

9.7.1 Merkkijonot

Jos kerrankin pääsisin vastakkain nykykielten kehittäjien kanssa, niin tekisi kovasti mieli kysyä ovatko he koskaan tehneet oikeaa ohjelmaa. Nimittäin lähes kielestä riippumatta kunnolliset merkkijonot loistavat poissaolollaan. Ja ohjelmoijat ovat käyttäneet äärettömästi työtunteja tehdessään itselleen aluksi edes auttavaa merkkijonokirjastoa. Ainoastaan "lelukielissä" - Basicissä ja Turbo Pascalissa on ollut hyvät ja turvalliset merkkijonot.

C- kielen char jono[10] on todellinen aikapommi, jonka aukkoisuuteen perustuu vielä tänäkin päivänä useat hakkereiden kikat murtautua vieraisiin tietojärjestelmiin. Katsotaanpa ensin mitä C- merkkijonoille voi/ei voi tehdä:

	char s1[10],s2[5],*p; 	:-(
	p = "Kana"           // Toimii!
	p[0] = 'S';          // Toimii! Mutta jatkossa käy huonosti...  
	s1 = "Kissa";        // ei toimi!
	strcpy(s2,"Koira");  // Huonosti käy!  Miksi?  Älä käytä koskaan...
	if ( s1 < s2 ) ...   // Sallittu, mutta tekee eri asian kuin lukija arvaakaan...
	gets(s1);            // Itsemurha, tämä on eräs kaikkein hirveimmistä funktioista
	                     // lukee päätteeltä rajattomasti merkkejä ...
	fgets(s1,sizeof(s1),stdin);  // Oikein!  Tosin rivinvaihto jää jonoon jos syöte
	                             // on lyhyempi kuin 9 merkkiä
	printf(s1);          // Ohohoh!   Tämä jopa toimii!!!
	cout << s1;          // Ja jopa tämäkin!!!
	cin >> s1;           // Taas itsemurha ....

Palaamme myöhemmin monisteessa C:n merkkijonoihin, ja siihen miten niitä voidaan kohtuullisen turvallisesti käyttää.

Onneksi C++:assa on kohtuullinen merkkijonoluokka. Nyt jo! Yli 10 vuotta kielen kehittämisen jälkeen...

Katso esimerkiksi: http://www.iki.fi/gaia/tekstit/cxxstring/

Merkkijonoluokalla voi tehdä mm. seuraavia:

\kurssit\cpp\ali\strtest.cpp - merkkijonojen testaus

	#include <iostream.h>
	#include <string>
	using namespace std;
	
	int main(void)
	{
	  string mjono1 = "Ensimmainen";
	  string mjono2 = "Toinen";
	  string mjono;
	
	// syotto ja tulostus
	  cout << "Anna merkkijono    > ";
	  getline(cin,mjono,'\n');
	  cout << "Annoit merkkijonon : " << mjono << endl;
	
	// kasittely merkeittain
	  mjono[0] = mjono1[4];
	  mjono[1] = mjono2[1];
	  mjono[2] = 't';
	  mjono[3] = '\0';       // Laitonta jos merkkijono ei ole nain iso!
	                         // Ei vaikuta!
	  cout << mjono << endl; // tulostaa: mot ...+jotakin
	
	// sijoitukset
	  mjono = mjono1;
	  cout << mjono << endl;       // Ensimmainen
	
	  mjono = "Eka";
	  cout << mjono << endl;       // Eka
	
	// katenointi
	  mjono = mjono1 + mjono2;
	  cout << mjono << endl;       // EnsimmainenToinen
	
	  mjono = mjono1 + "Toka";
	  cout << mjono << endl;       // EnsimmainenToka
	
	  mjono = "Eka" + mjono2;
	  cout << mjono << endl;       // EkaToinen
	
	// vertailut
	  if (mjono1 == mjono2) cout << "1 on 2" << endl;           // ei tulosta
	  if (mjono1 == "Apua") cout << "1 on Apua" << endl;        // ei tulosta
	  if ("Apua" == mjono2) cout << "Apua on 2" << endl;        // ei tulosta
	
	  if (mjono1 != mjono2) cout << "1 ei ole 2" << endl;       // 1 ei ole 2
	  if (mjono1 != "Apua") cout << "1 ei ole Apua" << endl;    // 1 ei ole Apua
	  if ("Apua" != mjono2) cout << "Apua ei ole 2" << endl;    // Apua ei ole 2
	
	  if (mjono1 < mjono2) cout << "1 pienempi kuin 2" << endl;     // 1 pienempi ku
	  if (mjono1 < "Apua") cout << "1 pienempi kuin Apua" << endl;  // ei tulosta
	  if ("Apua" < mjono2) cout << "Apua pienempi kuin 2" << endl;  // Apua pienempi
	
	  if (mjono1 > mjono2) cout << "1 suurempi kuin 2" << endl;     // ei tulosta
	  if (mjono1 > "Apua") cout << "1 suurempi kuin Apua" << endl;  // 1 suurempi ku
	  if ("Apua" > mjono2) cout << "Apua suurempi kuin 2" << endl;  // ei tulosta
	
	  if (mjono1 <= mjono2) cout << "1 pienempi tai yhtasuuri kuin 2" << endl;
	  if (mjono1 >= mjono2) cout << "1 suurempi tai yhtasuuri kuin 2" << endl;
	// Vastaavat vakiomerkkijonoilla  EI onnistu, koska vakiomerkkijonot ovat
	// OSOITTIMIA!
	  mjono1.erase(4,2);
	  cout << mjono1 << endl; // Ensiainen
	  mjono1.insert(4,"mm");
	  cout << mjono1 << endl; // Ensimmainen
	  return 0;
	}

Luokan string pitäisi tulla nykyisten C++- kääntäjien mukana. Se saadaan käyttöön lisäämällä koodin alkuun:

	#include <string>     // Otetaan käyttöön standardin merkkijonoluokka
	using namespace std;  // nyt ei tarvitse kirjoittaa std::string

Jos koneessa on niin vanha kääntäjä, ettei uudet luokat käänny sillä (ei esim. poikkeutusten käsittelyä), voi käyttää \kurssit\c\ali hakemistosta löytyvää "lasten" vastinetta, string, eli em. testiohjelma toimii jos otsikkotiedostojen hakupolkuun lisätään mainittu ali- hakemisto.

Tehtävä 9.90 Ensimäinen melkein järkevä olio

Täydennä seuraava ohjelma

olioalk\oppilas.cpp - 1. järkevä olio

	// Taydenna ja korjaile.  Mista puuttuu virtual?  Mista const?
	// Mita metodeja viela puuttuu? 
	#include <iostream.h>
	#include <string>
	using namespace std;
	
	class cHenkilo {
	  string nimi;
	  int ika;
	  double pituus_m;
	public:
	  cHenkilo(string inimi="???",int iika=0,int ipituus=0) {}
	  void tulosta()          {}
	  void kasvata(double cm) {}
	};
	
	class cOpiskelija : public cHenkilo {
	  double keskiarvo;
	public:
	  cOpiskelija(string inimi="???",int iika=0, int ipituus=0, int ikarvo=0.0) :
	    cHenkilo(inimi,iika,ipituus), keskiarvo(ikarvo) {}
	};
	
	int main(void)
	{
	  cHenkilo Kalle("Kalle",35,1.75);
	  Kalle.tulosta();
	  Kalle.kasvata(2.3);
	  Kalle.tulosta();
	  cOpiskelija Ville("Ville",21,1.80,9.9);
	  Ville.tulosta();
	  return 0;
	}


Ylös Edellinen Seuraava Otsikkosivu Hakemisto Sisällys