[C++] Implementazione classe
Stavo implementando una classe per matrici e per le operazioni tra di esse:
Il codice è il seguente:
HEADER matrici
IMPLEMENTAZIONE matrice:
HEADER operazioni:
IMPLEMENTAZIONE operazioni:
CODICE di prova:
Il mistero è che il codice di prova esce misteriosamente alla linea prima del commento \\DEBUG senza segnalare errori come se il resto del codice non esistesse. Qualcuno ha idea del perché?[/code]
Il codice è il seguente:
HEADER matrici
//MATRIX DATA STRUCTURE HEADER //V 1.0 #ifndef MATRIX_H #define MATRIX_H class Matrix { public: Matrix(int, int); //constructor ~Matrix(); //destructor int rows(); //return the number of rows int columns(); //return the number of columns double get(int, int); //get the element m[i,j] void set(int, int, double); //set the element m[i,j] void print(); //print a matrix private: double** matrix; int r; int c; }; #endif
IMPLEMENTAZIONE matrice:
//MATRIX DATA STRUCTURE CLASS //V 1.0 #include <iostream> #include <iomanip> #include "matrix.h" using namespace std; /*** Public section ***/ //default constructor Matrix::Matrix(int a, int b) { r = a; c = b; matrix = new double*[r]; for (int i = 0; i < r; i++) matrix[i] = new double[c]; for (int i = 0; i < r; i++) for (int j = 0; j < c; j++) matrix[i][j] = 0; } Matrix::~Matrix() { for (int i = 0; i < r; i++) delete [] matrix[i]; delete [] matrix; } int Matrix::rows() { return r; } int Matrix::columns() { return c; } double Matrix::get(int a, int b) { if ((a > r - 1) || (b > c - 1)) { cout << "ERROR: index out of bound"; return -1; } else return matrix[a][b]; } void Matrix::set(int a, int b, double value) { if ((a > r - 1) || (b > c - 1)) cout << "ERROR: index out of bound"; else matrix[a][b] = value; } void Matrix::print() { for (int i = 0; i < r; i++) { cout << endl; for (int j = 0; j < c; j++) { if (j != 0) cout << "\t" << matrix[i][j]; else cout << matrix[i][j]; } } }
HEADER operazioni:
//MATRIX OPERATIONS LIBRARY HEADER //V 1.0 #ifndef MATRIXMATH_H #define MATRIXMATH_H #include "Matrix.h" class matrixMath { public: matrixMath(); //constructor Matrix *transpose(Matrix *); //compute the transpose (STATIC) Matrix *sum(Matrix *, Matrix *); //compute the sum of two matrices (STATIC) Matrix *scalar(Matrix *, double); //multiply a scalar with the current matrix (STATIC) Matrix *product(Matrix *, Matrix *); //compute the product of two matrices (STATIC) }; #endif
IMPLEMENTAZIONE operazioni:
//MATRIX OPERATION LIBRARY SOURCE CODE //V 1.0 #include <iostream> #include "matrixMath.h" using namespace std; matrixMath::matrixMath() {}; Matrix *matrixMath::transpose(Matrix *a) { Matrix *t = new Matrix(a -> columns(), a -> rows()); for (int i = 0; i < a -> rows(); i++) for (int j = 0; j < a -> columns(); j++) t -> set(j, i, a -> get(i, j)); return t; } Matrix *matrixMath::sum(Matrix *a, Matrix *b) { if ((a -> rows() == b -> rows()) && (a -> columns() == b -> columns())) { Matrix *s = new Matrix(a -> rows(), a -> columns()); for (int i = 0; i < a -> rows(); i++) for (int j = 0; j < a -> columns(); j++) s -> set(i, j, a -> get(i,j) + b -> get(i,j)); return s; } else { Matrix *s = NULL; cout << "ERROR: only two NxM matrices can be summed \n\n" << endl; return s; } } Matrix *matrixMath::scalar(Matrix *a, double scalar) { Matrix *s = new Matrix (a -> rows(), a -> columns()); for (int i = 0; i < a -> rows(); i++) for (int j = 0; j < a -> columns(); j++) s -> set(i, j, a -> get(i,j) * scalar); return s; } Matrix *matrixMath::product(Matrix *a, Matrix *b) { if (a -> columns() == b -> rows()) { Matrix *p = new Matrix(a -> rows(), b -> columns()); for (int i = 0; i < a -> rows(); i++) for (int j = 0; j < b -> columns(); j++) for (int k = 0; k < b -> rows(); k++) p -> set(i, j, p -> get(i,j) + a -> get(i,k) * b -> get(k,j)); return p; } else { Matrix *p = NULL; cout << "ERROR: the two matrices are incompatible"; return p; } }
CODICE di prova:
#include <iostream> #include <cstdlib> #include <ctime> #include "matrixMath.h" using namespace std; void setMatrix(Matrix *m) { for (int i = 0; i < m -> rows(); i++) for (int j = 0; j < m -> columns(); j++) m -> set(i, j, rand() % 100); } int main() { srand(time(0)); cout << "Generating matrix... \n\n"; int rows = 2 + rand() % 8; int columns = 2 + rand() % 8; Matrix *matrixTest = new Matrix(rows, columns); setMatrix(matrixTest); matrixMath *math = new matrixMath(); cout << "Setting up matrix of " << rows << " rows and " << columns << " columns \n\n"; cout << "Printing Matrix... \n\n"; matrixTest -> print(); //DEBUG cout << "I'm here!" << endl; cout << "Testing transpose... \n\n"; Matrix *t = math -> transpose(matrixTest); cout << "TRANSPOSE: \n\n"; t -> print(); t = math -> scalar(matrixTest, 34.75); cout << "\n\n"; t -> print(); Matrix *a = new Matrix(rows, columns); Matrix *b = new Matrix(rows, columns); Matrix *c = new Matrix(columns, 2 + rand() % 8); setMatrix(a); setMatrix(b); setMatrix(c); cout << "Computing sum... \n\n"; a -> print(); cout << "\n\n"; b -> print(); cout << "\n\n"; Matrix *m = math -> sum(a, b); cout << "SUM: \n\n"; m -> print(); cout << "\n\n"; m = math -> prod(a, c); cout << "PROD: \n\n" << endl; m -> print(); delete a; delete b; delete c; delete t; delete m; delete matrixTest; return 0; }
Il mistero è che il codice di prova esce misteriosamente alla linea prima del commento \\DEBUG senza segnalare errori come se il resto del codice non esistesse. Qualcuno ha idea del perché?[/code]
Risposte
Ci sono diverse cose nel tuo codice che sono molto comuni in linguaggi come Java o C# ma che non si fanno quando si programma in C++ perché ci sono modi molto migliori per farlo.
In particolare noto il seguente errore: le funzioni per eseguire le operazioni tra matrici sono funzioni membro non statiche di una classe che non è neanche la classe Matrix. In C++ non solo non è assolutamente necessario avere funzioni membro ma è anzi spesso meglio avere funzioni "libere" in quanto più flessibili (possono essere usate in situazioni in cui una funzione membro non può essere usata). Inoltre nella tua libreria è necessario dichiarare una variabile di tipo matrixMath per fare operazioni tra matrici, non vedo alcuna motivazioni per questa enorme limitazione. Quelle funzioni non dipendono da nessuno stato e la classe non ha variabili membro, è insomma solo un contenitore di funzioni. Avresti almeno dovuto dichiararle statiche. Così stai solo rendendo molto più confuso il codice in quanto le funzioni per eseguire le operazioni verranno richiamate da classi con nome diverso in diverse occasioni anche se fanno esattamente la stessa cosa ma qualcuno che leggesse il tuo codice penserebbe ci siano delle differenze. Se proprio vuoi metterle come funzioni membro di qualche classe mettile come funzione membro di Matrix. In caso contrario mettile al di fuori di ogni classe (al massimo all'interno di un namespace).
Cerca di evitare inoltre, se possibile l'uso dell'allocazione dinamica della memoria e in particolare l'uso dei puntatori. reference e smart pointer sono spesso soluzioni migliori.
In particolare noto il seguente errore: le funzioni per eseguire le operazioni tra matrici sono funzioni membro non statiche di una classe che non è neanche la classe Matrix. In C++ non solo non è assolutamente necessario avere funzioni membro ma è anzi spesso meglio avere funzioni "libere" in quanto più flessibili (possono essere usate in situazioni in cui una funzione membro non può essere usata). Inoltre nella tua libreria è necessario dichiarare una variabile di tipo matrixMath per fare operazioni tra matrici, non vedo alcuna motivazioni per questa enorme limitazione. Quelle funzioni non dipendono da nessuno stato e la classe non ha variabili membro, è insomma solo un contenitore di funzioni. Avresti almeno dovuto dichiararle statiche. Così stai solo rendendo molto più confuso il codice in quanto le funzioni per eseguire le operazioni verranno richiamate da classi con nome diverso in diverse occasioni anche se fanno esattamente la stessa cosa ma qualcuno che leggesse il tuo codice penserebbe ci siano delle differenze. Se proprio vuoi metterle come funzioni membro di qualche classe mettile come funzione membro di Matrix. In caso contrario mettile al di fuori di ogni classe (al massimo all'interno di un namespace).
Cerca di evitare inoltre, se possibile l'uso dell'allocazione dinamica della memoria e in particolare l'uso dei puntatori. reference e smart pointer sono spesso soluzioni migliori.
Nel tuo codice c'è un errore di compilazione. Nel main hai scritto math->prod ma dovrebbe essere math-product. Una volta corretto e compilato, il codice viene eseguito senza problemi sul mio computer (non ho controllato però i risultati delle operazioni).
Mi puoi spiegare come si dichiara una classe statica perché avevo pensato di usarle ma nel mio libro spiega solo come funzionano ma non la loro dichiarazione? E poi perché sbagliando il nome della funzione membro non dava errore di compilazione??
Sul mio computer quel programma non veniva compilato, non ho idea del motivo per cui il tuo compilatore non abbia trovato errori nel codice.
Per dichiarare una funzione come statica è sufficiente scrivere static quando definisci la funzione. Nel tuo caso (non mettere mai commenti visibilmente falsi, le funzioni prima non erano statiche):
Per chiamarle dovrai poi scrivere ad esempio:
Ma è meglio usare i namespace per fare queste cose (inserendo però le funzioni nello stesso namespace della classe Matrix (perché inserire un ulteriore livello?). In C++ si fa inoltre largo uso dell'overload di operatori: quindi è comune scrivere A + B invece che sum(A, B) e così via. In molte librerie che implementano matrici ho anche visto implementare l'operatore () in modo da accedere agli elementi della matrice usando m(i,j) invece che m.get(i,j) e m.set(i,j, v). Che libro stai usando?
Per dichiarare una funzione come statica è sufficiente scrivere static quando definisci la funzione. Nel tuo caso (non mettere mai commenti visibilmente falsi, le funzioni prima non erano statiche):
class matrixMath { public: static Matrix *transpose(Matrix *); //compute the transpose (STATIC) static Matrix *sum(Matrix *, Matrix *); //compute the sum of two matrices (STATIC) static Matrix *scalar(Matrix *, double); //multiply a scalar with the current matrix (STATIC) static Matrix *product(Matrix *, Matrix *); //compute the product of two matrices (STATIC) };
Per chiamarle dovrai poi scrivere ad esempio:
matrixMath::transpose(m);
Ma è meglio usare i namespace per fare queste cose (inserendo però le funzioni nello stesso namespace della classe Matrix (perché inserire un ulteriore livello?). In C++ si fa inoltre largo uso dell'overload di operatori: quindi è comune scrivere A + B invece che sum(A, B) e così via. In molte librerie che implementano matrici ho anche visto implementare l'operatore () in modo da accedere agli elementi della matrice usando m(i,j) invece che m.get(i,j) e m.set(i,j, v). Che libro stai usando?
C++ fondamenti di programmazione - H.M. Deitel, B.J. Deitel.
Si cmq so che si possono ridefinire gli operatori, ma sta classe non l'ho fatta perché mi serviva, era come esercizio. Le funzioni erano commentate statiche perché le volevo creare statiche. Ora so come si fa, grazie.
Si cmq so che si possono ridefinire gli operatori, ma sta classe non l'ho fatta perché mi serviva, era come esercizio. Le funzioni erano commentate statiche perché le volevo creare statiche. Ora so come si fa, grazie.
Ho appena scoperto che c'era spiegato come dichiarare le funzioni statiche, avevo mancato la pagina in tronco
