next up previous contents index
Next: Templates Up: Dettagli sulle classi. Templates Previous: Funzioni friend   Indice   Indice analitico

Un esempio concreto: stringhe

Nel capitolo 6 abbiamo presentato l'approccio stile-C all'utilizzo di stringhe di caratteri, il quale è poco intuitivo e facilmente soggetto ad errori; il C++ utilizza la classe String, contenuta nella libreria standard string 10.4. Noi tuttavia preferiamo reinventare la ruota, costruendo una nostra classe Stringa, che meglio si adatta agli scopi didattici di questo corso.

Implementeremo una Stringa con un array dinamico di caratteri e, oltre alla sovrapposizione degli operatori basilari (entrata, uscita, concatenazione), a titolo di esempio programmeremo alcune funzioni di utilità generale per una stringa, come la ricerca di sottostringhe e il conteggio di ricorrenze di caratteri in una stringa.


// stringa.h
// implementa un tipo stringa di caratteri

// le seguenti macro servono ad evitare inclusioni
// multiple del presente file di intestazione

#ifndef _STRINGA_H_
#define _STRINGA_H_

const unsigned int MAX_LUNGHEZZA = 100;

#include <iostream.h>

class Stringa {
  // funzioni friend per gestire l'i/o di stringhe
  friend ostream& operator<< (ostream&, const Stringa&);
  friend istream& operator>> (istream&, Stringa&);

  // funzioni friend per la sovrapposizione di operatori binari
  // concatenazione di stringhe
  friend Stringa operator+ (const Stringa& s1, const Stringa& s2);
  friend Stringa operator+ (const char* s1,    const Stringa& s2);
  friend Stringa operator+ (const Stringa& s1, const char*    s2);
  // eliminazione degli ultimi n caratteri di una stringa
  friend Stringa operator- (const Stringa& s, unsigned int n);
  // operatori di confronto
  friend bool operator== (const Stringa& s1, const Stringa& s2);
  friend bool operator< (const Stringa& s1, const Stringa& s2);
  friend bool operator> (const Stringa& s1, const Stringa& s2) {
    if (!(s1 < s2) && !(s1 == s2)) return true;
    return false; }
  friend bool operator<= (const Stringa& s1, const Stringa& s2) {
    if (s1 < s2 || s1 == s2) return true;
    return false; }
  friend bool operator>= (const Stringa& s1, const Stringa& s2) {
    if (!(s1 < s2)) return true;
    return false; }
  friend bool operator!= (const Stringa& s1, const Stringa& s2) {
    if (!(s1 == s2)) return true;
    return false; }
public:
  // costruttore default
  Stringa (const char* s = 0);
  // costruttore di copia
  Stringa (const Stringa&);
  // sovrapposizione dell'operatore di assegnamento
  const Stringa& operator= (const Stringa&);
  // distruttore
  ~Stringa ();

  // sovrapposizione di alcuni operatori comuni
  // concatena una nuova stringa alla preesistente
  Stringa& operator+= (const char* s);
  Stringa& operator+= (const Stringa&);
  // elimina gli ultimi n caratteri dalla stringa
  Stringa& operator-= (unsigned int n);
  // crea una stringa formata da n ripetizioni
  // della stringa preesistente
  Stringa& operator*= (unsigned int n);

  // funzioni di utilita`
  // ritorna la lunghezza della stringa (senza '\0')
  unsigned int lunghezza () const { return len; }
  // operatore di conversione a const char*
  operator const char* () const { return stringa; }
  // conversioni MAIUSCOLO <--> minuscolo
  Stringa& maiuscolo ();
  Stringa& minuscolo ();
  // inverte la stringa
  Stringa& inverti ();
  // ricerca di una sottostringa
  int trova (const Stringa&) const;
  // estrazione sinistra di una sottostringa
  // ritorna la sottostringa estratta
  Stringa estrai (unsigned int pos, unsigned int lung);
  // conteggio di una stringa ricorrente
  unsigned int conta (const Stringa&) const;
private:
  char* stringa;
  unsigned int len;
};

#endif // _STRINGA_H_


// stringa.cpp

#include "stringa.h"
#include <string.h>

Stringa::Stringa (const char* s) {
  if (s == 0) {
    len = 0;
    stringa = 0;
  }
  else {
    len = strlen(s);
    stringa = new char[len + 1];
    for (int i = 0; i < len; i++)
      stringa[i] = s[i];
    stringa[len] = '\0';
  }
}

Stringa::Stringa (const Stringa& s) {
  len = s.len;
  if (s.stringa == 0)
    stringa = 0;
  else {
    stringa = new char[len + 1];
    for (int i = 0; i < len; i++)
      stringa[i] = s[i];
    stringa[len] = '\0';
  }
}

const Stringa& Stringa::operator= (const Stringa& s) {
  // evita l'autoassegnamento
  if (&s != this) {
    if (len != s.len) {
      len = s.len;
      delete[] stringa;
      stringa = new char[len + 1];
    }
    for (int i = 0; i < len; i++)
      stringa[i] = s[i];
    stringa[len] = '\0';
  }
  return *this;
}

Stringa::~Stringa () {
  delete[] stringa;
}

Stringa& Stringa::operator+= (const char* s) {
  if (s != 0) {
    // crea una copia temporanea di back up
    Stringa tmp(*this);
    int i;  // contatore
    len += strlen(s);
    delete[] stringa;
    stringa = new char[len + 1];
    for (i = 0; i < tmp.len; i++)
      stringa[i] = tmp.stringa[i];
    for (i = 0; i < strlen(s); i++)
      stringa[i + tmp.len] = s[i];
    stringa[len] = '\0';
  }
  return *this;
}

Stringa& Stringa::operator+= (const Stringa& s) {
  if (s.stringa != 0) {
    // crea una copia temporanea di back up
    Stringa tmp(*this);
    int i;  // contatore
    len += s.len;
    delete[] stringa;
    stringa = new char[len + 1];
    for (i = 0; i < tmp.len; i++)
      stringa[i] = tmp.stringa[i];
    for (i = 0; i < s.len; i++)
      stringa[i + tmp.len] = s.stringa[i];
    stringa[len] = '\0';
  }
  return *this;
}

Stringa& Stringa::operator-= (unsigned int n) {
  if (len > n) {
    // crea una copia temporanea di back up
    Stringa tmp(*this);
    delete[] stringa;
    len -= n;
    stringa = new char[len + 1];
    for (int i = 0; i < len; i++)
      stringa[i] = tmp.stringa[i];
    stringa[len] = '\0';
  }
  else {
    delete[] stringa;
    stringa = 0;
    len = 0;
  }
  return *this;
}

Stringa& Stringa::operator*= (unsigned int n) {
  if (n > 1) {
    // crea una copia temporanea di back up
    Stringa tmp(*this);
    delete[] stringa;
    len *= n;
    stringa = new char[len + 1];
    for (int i = 0; i < len; i++)
      stringa[i] = tmp.stringa[i % tmp.len];
    stringa[len] = '\0';
  }
  return *this;
}

Stringa& Stringa::minuscolo() {
  for (int i = 0; i < len; i++)
    if (stringa[i] >= 65 && stringa[i] <= 90)
      stringa[i] += 32;
  return *this;
}

Stringa& Stringa::maiuscolo() {
  for (int i = 0; i < len; i++)
    if (stringa[i] >= 91 && stringa[i] <= 116)
      stringa[i] -= 32;
  return *this;
}

Stringa& Stringa::inverti() {
  if (stringa != 0) {
    for (int i = 0; i < len / 2; i++) {
      char tmp = stringa[i];
      stringa[i] = stringa[len - i - 1];
      stringa[len - i - 1] = tmp;
    }
  }
  return *this;
}

int Stringa::trova (const Stringa& s) const {
  if (len > s.len) {
    for (int i = 0; i < len - s.len; i++) {
      bool uguale = true;
      for (int j = 0; j < s.len; j++)
        if (stringa[i + j] != s.stringa[j])
          uguale = false;
      if (uguale == true)
        return i;
    }
  }
  else if (s.len == len)
    if (s == *this)
      return 0;
  return -1;
}

Stringa Stringa::estrai (unsigned int pos, unsigned int lung) {
  Stringa tmp;
  if (lung < len) {
    tmp.len = lung;
    tmp.stringa = new char[tmp.len + 1];
    for (int i = 0; i < lung; i++)
      tmp.stringa[i] = stringa[i+pos];
  }
  return tmp;
}

unsigned int Stringa::conta (const Stringa& s) const {
  int r = 0;  // ricorrenze trovate
  if (s.stringa != 0) {
    for (int i = 0; i < len - s.len; i++) {
      bool uguale = true;
      for (int j = 0; j < s.len; j++)
        if (stringa[i + j] != s.stringa[j])
          uguale = false;
      if (uguale == true) r++;
    }
  }
  return r;
}

Stringa operator+ (const Stringa& s1, const Stringa& s2) {
  Stringa tmp;
  int i;   // variabile contatore
  tmp.len = s1.len + s2.len;
  tmp.stringa = new char[tmp.len + 1];
  for (i = 0; i < s1.len; i++)
    tmp.stringa[i] = s1.stringa[i];
  for (i = 0; i < s2.len; i++)
    tmp.stringa[s1.len + i] = s2.stringa[i];
  tmp.stringa[s1.len + s2.len] = '\0';
  return tmp;
}

Stringa operator+ (const Stringa& s1, const char* s2) {
  Stringa tmp;
  int i;   // variabile contatore
  tmp.len = s1.len + strlen(s2);
  tmp.stringa = new char[tmp.len + 1];
  for (i = 0; i < s1.len; i++)
    tmp.stringa[i] = s1.stringa[i];
  for (i = 0; i < strlen(s2); i++)
    tmp.stringa[s1.len + i] = s2[i];
  tmp.stringa[s1.len + strlen(s2)] = '\0';
  return tmp;
}

Stringa operator+ (const char* s1, const Stringa& s2) {
  Stringa tmp;
  int i;   // variabile contatore
  tmp.len = strlen(s1) + s2.len;
  tmp.stringa = new char[tmp.len + 1];
  for (i = 0; i < strlen(s1); i++)
    tmp.stringa[i] = s1[i];
  for (i = 0; i < s2.len; i++)
    tmp.stringa[strlen(s1) + i] = s2.stringa[i];
  tmp.stringa[strlen(s1) + s2.len] = '\0';
  return tmp;
}

Stringa operator- (const Stringa& s, unsigned int n) {
  Stringa tmp;
  if (s.len > n) {
    tmp.len = s.len - n;
    tmp.stringa = new char[tmp.len + 1];
    for (int i = 0; i < tmp.len; i++)
      tmp.stringa[i] = s.stringa[i];
    tmp.stringa[tmp.len] = '\0';
  }
  return tmp;
}

bool operator== (const Stringa& s1, const Stringa& s2) {
  if (s1.len == s2.len) {
    for (int i = 0; i < s1.len; i++)
      if (s1.stringa[i] != s2.stringa[i])
        return false;
    return true;
  }
  return false;
}

bool operator< (const Stringa& s1, const Stringa& s2) {
  int min;
  if (s1.len <= s2.len) min = s1.len;
  else min = s2.len;
  for (int i = 0; i < min; i++) {
    if (s1.stringa[i] < s2.stringa[i])
      return true;
  }
  return false;
}

ostream& operator<< (ostream& os, const Stringa& s) {
  if (s.stringa != 0)
    os << (const char*)s;
  return os;
}

istream& operator>> (istream& is, Stringa& s) {
  char buffer[MAX_LUNGHEZZA + 1];
  cin >> buffer;
  s = Stringa(buffer);
  return is;
}

Un programma di esempio, che testi la maggior parte delle funzioni della classe su presentata, è il seguente:


// ex10_4_1

#include "stringa.h"

void main() {
  Stringa a, b("Bjarne Stroustrup"), c(b);
  a = b;
  cout << a << "\n";
  cin >> a;
  cout << c + a << "\n";
  cout << c + " e' il padre del C++" << "\n";
  cout << "il C++ e' nato grazie a " + c << "\n";
  cout << b - 5 << "\n";
  a = Stringa("Philip");
  a += " Zimmermann";
  a += Stringa(" e' il padre del PGP (pretty good privacy)");
  a -= 22;
  cout << a << "\n";
  c = "*-";
  c *= 10;
  cout << c << "\n";
  cout << a.maiuscolo() << "\n";
  cout << a.minuscolo() << "\n";
  b = "sdlavroT";
  cout << b.inverti() << "\n";
  cout << b.estrai(b.trova("val"), 3) << "\n";
  c = "Perdindirindina";
  cout << c.conta(Stringa("di")) << "\n";
}

esempio di output:
Bjarne Stroustrup
!!
Bjarne Stroustrup!!
Bjarne Stroustrup e' il padre del C++
il C++ e' nato grazie a Bjarne Stroustrup
Bjarne Strou
Philip Zimmermann e' il padre del PGP
*-*-*-*-*-*-*-*-*-*-
PHILIP ZIMMERMANN E' IL PADRE DEL PGP
philip zimmermann e' il padre del pgp
Torvalds
val
3

Si consiglia di provare ad implementare un maggior numero di funzioni nella classe Stringa, al fine di prendere confidenza con la gestione delle stringhe e con la progettazione di classi in C++.


next up previous contents index
Next: Templates Up: Dettagli sulle classi. Templates Previous: Funzioni friend   Indice   Indice analitico
Claudio Cicconetti
2000-09-06