[C++] Implementazione classe

Ext3rmin4tor
Stavo implementando una classe per matrici e per le operazioni tra di esse:

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
apatriarca
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.

apatriarca
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).

Ext3rmin4tor
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??

apatriarca
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):
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?

Ext3rmin4tor
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.

Ext3rmin4tor
Ho appena scoperto che c'era spiegato come dichiarare le funzioni statiche, avevo mancato la pagina in tronco :oops:

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.