Matrice
, che
utilizzeremo successivamente per disegnare la classe specifica
MatriceReale
. Abbiamo spesso lavorato su matrici, per cui non ci
dilungheremo su dettagli già chiariti riguardo il loro utilizzo.
// tabella.h // classe template rappresentante una tabella #ifndef _TABELLA_H_ #define _TABELLA_H_ template<class T> class Tabella { public: Tabella (unsigned int r, unsigned int c, bool p = true); Tabella (const Tabella&); const Tabella& operator= (const Tabella&); ~Tabella (); bool possesso () const { return possiede; } unsigned int nrighe () const { return righe; } unsigned int ncolonne () const { return colonne; } Tabella& ridimensiona (unsigned int r, unsigned int c); Tabella& inserisciRiga (unsigned int r); Tabella& inserisciColonna (unsigned int c); Tabella& eliminaRiga (unsigned int r); Tabella& eliminaColonna (unsigned int c); T* elemento (unsigned int i, unsigned int j) const { if (i > righe || j > colonne) return 0; return tabella[indice(i, j)]; } bool modifica (unsigned int i, unsigned int j, T* oggetto) { if (i > righe || j > colonne) return false; if (possiede == true) delete tabella[indice(i, j)]; tabella[indice(i, j)] = oggetto; return true; } private: T** tabella; unsigned int righe; unsigned int colonne; bool possiede; // funzioni interne per facilitare l'accesso alla tabella // restituisce l'indice (i,j) della tabella *this unsigned int indice (unsigned int i, unsigned int j) const { return ((i - 1) * colonne) + j - 1; } // restituisce l'indice (i,j) di una tabella // avente c colonne unsigned int indice (unsigned int i, unsigned int j, unsigned int c) const { return ((i - 1) * c) + j - 1; } }; template<class T> Tabella<T>::Tabella (unsigned int r, unsigned int c, bool p) { righe = r; colonne = c; possiede = p; // crea l'array che conterra` la tabella ... tabella = new T*[righe * colonne]; // ... e lo azzera for (int i = 0; i < righe * colonne; i++) tabella[i] = 0; } template<class T> Tabella<T>::Tabella (const Tabella& t) { righe = t.righe; colonne = t.colonne; // ATTENZIONE le copie di oggetti esistenti NON // possiedono il loro contenuto di default tabella = new T*[righe * colonne]; possiede = false; for (int i = 0; i < righe * colonne; i++) tabella[i] = t.tabella[i]; } template<class T> const Tabella<T>& Tabella<T>::operator= (const Tabella& t) { if (&t != this) { // ATTENZIONE le copie di oggetti esisteni NON // possiedono il loro contenuto di default possiede = false; if (possiede == true) { for (int i = 0; i < righe * colonne; i++) delete tabella[i]; } if ((righe * colonne) != (t.righe * t.colonne)) { if (righe != t.righe) { // colonne != t.colonne righe = t.righe; colonne = t.colonne; } // riallochiamo l'array delete[] tabella; righe = t.righe; colonne = t.colonne; tabella = new T*[righe * colonne]; } for (int i = 0; i < righe * colonne; i++) tabella[i] = t.tabella[i]; } return *this; } template<class T> Tabella<T>::~Tabella () { if (possiede == true) for (int i = 0; i < righe * colonne; i++) delete tabella[i]; delete[] tabella; } template<class T> Tabella<T>& Tabella<T>::ridimensiona (unsigned int r, unsigned int c) { if (r != righe || c != colonne) { // allochiamo memoria per la nuova tabella T** nuova_tabella = new T*[r * c]; for (int i = 1; i <= r; i++) for (int j = 1; j <= c; j++) if (i <= r && j <= c) if (i <= righe && j <= colonne) nuova_tabella[indice(i, j, c)] = tabella[indice(i,j)]; else nuova_tabella[indice(i, j, c)] = 0; for (int i = 1; i <= r; i++) for (int j = 1; j <= c; j++) if (i > r || j > c) if (possiede == true) delete tabella[indice(i, j)]; else tabella[indice(i, j)] = 0; delete[] tabella; tabella = nuova_tabella; righe = r; colonne = c; } return *this; } template<class T> Tabella<T>& Tabella<T>::inserisciRiga (unsigned int r) { if (r > 0) { if (r > righe) r = righe + 1; T** nuova_tabella = new T*[(righe+1) * colonne]; for (int i = 1; i <= r-1; i++) for (int j = 1; j <= colonne; j++) nuova_tabella[indice(i, j)] = tabella[indice(i, j)]; for (int j = 1; j <= colonne; j++) nuova_tabella[indice(r, j)] = 0; for (int i = r+1; i <= righe+1; i++) for (int j = 1; j <= colonne; j++) nuova_tabella[indice(i, j)] = tabella[indice(i-1, j)]; delete[] tabella; tabella = nuova_tabella; righe++; } return *this; } template<class T> Tabella<T>& Tabella<T>::inserisciColonna (unsigned int c) { if (c > 0) { if (c > colonne) c = colonne +1; T** nuova_tabella = new T*[righe * (colonne+1)]; for (int i = 1; i <= righe; i++) for (int j = 1; j <= c-1; j++) nuova_tabella[indice(i, j, colonne+1)] = tabella[indice(i, j)]; for (int i = 1; i <= righe; i++) nuova_tabella[indice(i, c, colonne+1)] = 0; for (int i = 1; i <= righe; i++) for (int j = c+1; j <= colonne+1; j++) nuova_tabella[indice(i, j, colonne+1)] = tabella[indice(i, j-1)]; delete[] tabella; tabella = nuova_tabella; colonne++; } return *this; } template<class T> Tabella<T>& Tabella<T>::eliminaRiga (unsigned int r) { if (r > 0 && r <= righe) { T** nuova_tabella = new T*[(righe - 1) * colonne]; for (int i = 1; i <= righe-1; i++) for (int j = 1; j <= colonne; j++) if (i <= r-1) nuova_tabella[indice(i, j)] = tabella[indice(i, j)]; else nuova_tabella[indice(i, j)] = tabella[indice(i+1, j)]; if (possiede == true) for (int j = 1; j <= colonne; j++) delete tabella[indice(r, j)]; righe--; delete[] tabella; tabella = nuova_tabella; } return *this; } template<class T> Tabella<T>& Tabella<T>::eliminaColonna (unsigned int c) { if (c > 0 && c <= colonne) { T** nuova_tabella = new T*[righe * (colonne-1)]; for (int i = 1; i <= righe; i++) for (int j = 1; j <= colonne-1; j++) if (j <= c-1) nuova_tabella[indice(i, j, colonne-1)] = tabella[indice(i, j)]; else nuova_tabella[indice(i, j, colonne-1)] = tabella[indice(i, j+1)]; if (possiede == true) for (int i = 1; i <= righe; i++) delete tabella[indice(i, c)]; colonne--; delete[] tabella; tabella = nuova_tabella; } return *this; } #endif // _TABELLA_H_
Le funzioni più interessanti sono quelle riguardanti l'inserimento o l'eliminazione di una riga o una colonna; il procedimento seguito è sempre il medesimo:
nuova_tabella
;righe
e
colonne
);tabella
l'array nuova_tabella
.
Si noti che la funzione indice(i,j)
suppone che il numero di colonne
sia il medesimo della tabella di *
this, il che non è vero
nel caso di eliminazione o inserimento di riga; all'interno di tali procedure
abbiamo utilizzato dunque la funzione membro privata indice(i,j,c)
,
alla quale passiamo come terzo argomento il numero di colonne della tabella
della quale vogliamo ottenere un indice. Un semplice programma che testi la
classe Tabella
è il seguente:
// ex11_4_1.cpp #include <iostream.h> #include "stringa.h" #include "tabella.h" ostream& operator<< (ostream& os, Tabella<Stringa>& t) { for (int i = 1; i <= t.nrighe(); i++) { int j; for (j = 1; j < t.ncolonne(); j++) if (t.elemento(i, j) != 0) os << *t.elemento(i, j) << "\t"; else os << "-\t"; if (t.elemento(i, j) != 0) os << *t.elemento(i, j); else os << "-\t"; cout << "\n"; } return os; } void main() { const int r = 3; const int c = 4; Tabella<Stringa> t(r, c); t.modifica(1,1, new Stringa("(1,1)")); t.modifica(1,c, new Stringa("adx")); t.modifica(r,1, new Stringa("bsx")); t.modifica(r,c, new Stringa("ultimo")); cout << t << "\n"; t.ridimensiona(2*r, c); t.modifica(2,1, new Stringa("riga2")); t.modifica(3,1, new Stringa("riga3")); t.modifica(1,2, new Stringa("col2")); t.modifica(1,3, new Stringa("col3")); cout << t << "\n"; t.inserisciRiga(3); t.inserisciColonna(3); cout << t << "\n"; t.eliminaRiga(3); t.eliminaColonna(3); cout << t << "\n"; }
output:
(1,1) - - adx - - - - bsx - - ultimo (1,1) col2 col3 adx riga2 - - - riga3 - - ultimo - - - - - - - - - - - - (1,1) col2 - col3 adx riga2 - - - - - - - - - riga3 - - - ultimo - - - - - - - - - - - - - - - (1,1) col2 col3 adx riga2 - - - riga3 - - ultimo - - - - - - - - - - - -
Proviamo ora a programmare una classe che rappresenti una matrice di numeri
reali, sfruttando la classe template Tabella
che abbiamo appena
completato. Il file di intestazione è il seguente:
// matrice.h // implementa una matrice reale utilizzando // la classe template Tabella #ifndef _MATRICE_H_ #define _MATRICE_H_ #include <iostream.h> #include "tabella.h" class Matrice { public: // costruttori, operatore=, distruttore Matrice (unsigned int r, unsigned int c, // valore iniziale elementi sulla diagonale double elem_diag = 0.0, // valore iniziale elementi non diagonali double elem_nondiag = 0.0); Matrice (const Matrice&); const Matrice& operator= (const Matrice&); ~Matrice (); // ridimensionamento matrice Matrice& ridimensiona (unsigned int r, unsigned int c) { matrice->ridimensiona (r, c); return *this; } Matrice& inserisciRiga (unsigned int r) { matrice->inserisciRiga (r); return *this; } Matrice& inserisciColonna (unsigned int c) { matrice->inserisciColonna (c); return *this; } Matrice& eliminaRiga (unsigned int r) { matrice->eliminaRiga (r); return *this; } Matrice& eliminaColonna (unsigned int c) { matrice->eliminaColonna (c); return *this; } // funzioni di accesso unsigned nrighe() const { return matrice->nrighe(); } unsigned ncolonne() const { return matrice->ncolonne(); } double elemento (unsigned int i, unsigned int j) const { if (i > 0 && i <= matrice->nrighe() && j > 0 && j <= matrice->ncolonne()) if (matrice->elemento(i,j) != 0) return *matrice->elemento (i, j); return 0; } bool modifica (unsigned int i, unsigned int j, double x) { if (i > 0 && i <= matrice->nrighe() && j > 0 && j <= matrice->ncolonne()) { matrice->modifica(i, j, new double(x)); return true; } return false; } // funzioni algebriche sulle matrici Matrice operator+ (const Matrice& m) const; Matrice operator- (const Matrice& m) const; Matrice operator- () const; Matrice operator* (const Matrice& m) const; Matrice operator* (double x) const; private: Tabella<double>* matrice; }; ostream& operator<< (ostream& os, const Matrice& m); #endif // _MATRICE_H_
Mentre le definizioni sono contenute nel file Matrice.cpp
:
// matrice.cpp // intestazione della classe in matrice.h #include "matrice.h" Matrice::Matrice (unsigned int r, unsigned int c, double elem_diag, double elem_nondiag) { matrice = new Tabella<double>(r, c, true); for (int i = 1; i <= r; i++) for (int j = 1; j <= c; j++) if (i == j) matrice->modifica (i, j, new double(elem_diag)); else matrice->modifica (i, j, new double(elem_nondiag)); } Matrice::Matrice (const Matrice& m) { matrice = new Tabella<double>(*m.matrice); } const Matrice& Matrice::operator= (const Matrice& m) { if (&m != this) { delete matrice; matrice = new Tabella<double>(*m.matrice); } return *this; } Matrice::~Matrice () { delete matrice; } Matrice Matrice::operator+ (const Matrice& m) const { Matrice tmp(nrighe(), ncolonne()); if (nrighe() == m.nrighe() && ncolonne() == m.ncolonne()) { for (int i = 1; i <= nrighe(); i++) for (int j = 1; j <= ncolonne(); j++) tmp.modifica (i, j, elemento(i ,j) + m.elemento(i ,j)); } return tmp; } Matrice Matrice::operator- (const Matrice& m) const { Matrice tmp(nrighe(), ncolonne()); if (nrighe() == m.nrighe() && ncolonne() == m.ncolonne()) { for (int i = 1; i <= nrighe(); i++) for (int j = 1; j <= ncolonne(); j++) tmp.modifica (i, j, elemento(i ,j) - m.elemento(i ,j)); } return tmp; } Matrice Matrice::operator- () const { Matrice tmp(nrighe(), ncolonne()); for (int i = 1; i <= nrighe(); i++) for (int j = 1; j <= ncolonne(); j++) tmp.modifica (i, j, -elemento(i ,j) ); return tmp; } Matrice Matrice::operator* (double x) const { Matrice tmp(nrighe(), ncolonne()); for (int i = 1; i <= nrighe(); i++) for (int j = 1; j <= ncolonne(); j++) tmp.modifica (i, j, elemento(i ,j) * x); return tmp; } Matrice Matrice::operator* (const Matrice& m) const { Matrice tmp(nrighe(), m.ncolonne()); if (ncolonne() == m.nrighe()) { for (int i = 1; i <= nrighe(); i++) for (int j = 1; j <= ncolonne(); j++) tmp.modifica (i, j, elemento(i ,j) + m.elemento(i ,j)); } return tmp; } ostream& operator<< (ostream& os, const Matrice& m) { for (int i = 1; i <= m.nrighe(); i++) { for (int j = 1; j <= m.ncolonne(); j++) if (j == m.ncolonne()) os << m.elemento(i, j); else os << m.elemento(i, j) << "\t"; os << "\n"; } return os; }
L'utilizzo di una classe del genere all'interno di un programma
matematico-scientifico non è certo vantaggioso, in quanto risulta piuttosto
macchinoso dovere allocare un array di puntatori a reali, piuttosto che
direttamente un array di reali; tuttavia è utile mostrare come si possa
sfruttare un contenitore di tipo Tabella
per scopi particolari. Un
esempio di utilizzo di Matrice
è:
// ex11_4_2.cpp #include "matrice.h" void main() { Matrice m(3, 4, 1, 0); for (int i = 1; i <= m.nrighe(); i++) for (int j = 1; j <= m.ncolonne(); j++) m.modifica (i, j, double((i-1) * m.ncolonne() + j)); cout << m << "\n"; m.inserisciRiga(2); m.inserisciColonna(3); cout << m << "\n"; m.eliminaRiga(1); m.eliminaColonna(1); cout << m << "\n"; Matrice a(3, 4, 1, 0); cout << m+a << "\n"; cout << m-a << "\n"; cout << -m << "\n"; cout << m * 3 << "\n"; }
output:
1 2 3 4 5 6 7 8 9 10 11 12 1 2 0 3 4 0 0 0 0 0 5 6 0 7 8 9 10 0 11 12 0 0 0 0 6 0 7 8 10 0 11 12 1 0 0 0 6 1 7 8 10 0 12 12 -1 0 0 0 6 -1 7 8 10 0 10 12 -0 0 -0 -0 -6 -0 -7 -8 -10 -0 -11 -12 0 0 0 0 18 0 21 24 30 0 33 36
Tabella
si programmi la classe
IperArray
costituita da una matrice di puntatori ad array
unidimensionali di numeri interi; si abbiano le seguenti funzioni membro:
IperArray (int r, int c, int n)
: costruttore; l'intero n
indica il numero di elementi che costituiscono gli array, posizionati nelle
celle di una matrice di r
righe per c
colonne;IperArray (const IperArray&)
: costruttore di copia;const IperArray& operator= (const IperArray&)
: sovrapposizione
dell'operatore di assegnamento;~IperArray()
: distruttore;bool modifica(int r, int c, int i, int n)
: modifica
l'i
-esimo elemento dell'array in posizione (r
,c
),
assegnando ad esso il valore n
; in caso di successo ritorna
true;int elemento(int r, int c, int i) const
: restituisce il valore
contenuto nella i
-esima cella dell'array in posizione
(r
,c
);int somma(int r, int c) const
: restituisce la somma dei valori
contenuti nell'array (r
,c
);int somma() const
: restituisce la somma dei valori contenuti
nelle celle di tutti gli array;int quanti() const
: restituisce il numero totale degli elementi
non-zero dell'IperArray
;IperArray operator+ (const IperArray&) const
: restituisce la
somma di due IperArray
; la somma si effettua elemento ad elemento su
tutti gli array;IperArray operator* (int n) const
: restituisce il
prodotto di un IperArray
per l'interno n
; il prodotto si
effettua elemento per elemento su tutti gli array;IperArray& operator++ ()
: incrementa di una unità ogni elemento
dell'IperArray
;