simoo88 (Normal User)
Newbie
Messaggi: 12
Iscritto: 15/05/2009
|
Buonasera a tutti,
vi scrivo perchè dovrei progettare e realizzare una classe generica che
implementa una Matrice di elementi di tipo T (usare i tamplate). Oltre ai normali metodi
fondamentali e tipici di una classe container.La classe deve includere il supporto agli iteratori di tipo forward.
Ora mi rivolgo a voi che sarete sicuramente + esperti di me per aiutarmi ad avere un'idea sul come progettarla.Grazie saluti.
|
|
simoo88 (Normal User)
Newbie
Messaggi: 12
Iscritto: 15/05/2009
|
ah ricordo che non posso usare librerie esterne e strutture dati container della std library come
std::vector, std::list e simili.
|
|
Poggi Marco (Member)
Guru
Messaggi: 969
Iscritto: 05/01/2010
|
Ciao!
Purtroppo, noi mon possiamo aiutarti più di tanto!
Non ci hai detto quale sia lo scopo della classe, e se hai già abbozzato qualcosa, postacelo descrivendo cosa non va!
Ultima modifica effettuata da Poggi Marco il 22/01/2010 alle 22:12 |
|
lorenzo (Normal User)
Guru
Messaggi: 1178
Iscritto: 15/04/2008
|
ah, i progetti universitari.....comunque se dobbiamo aiutarti dovresti postare il codice che hai già scritto
|
|
TheKaneB (Member)
Guru^2
Messaggi: 1792
Iscritto: 26/06/2009
|
Attenzione! Post chilometrico... si prega di allacciare le cinture e procurarsi un bel pacco di patatine da sgranocchiare durante la lettura...
Beh progettare un template come questo non è una cosa difficile... Devi pensare innanzitutto alla natura della matrice...
Ti faccio un esempio con una matrice generica 3x3
Codice sorgente - presumibilmente C/C++ |
// il file è matrix.h, ricordati che i template non vanno compilati come i .cpp!
template <typename T>
class Matrix33
{
public:
Matrix33<T>()
{
// Qui potresti inserire del codice per inizializzare la matrice
// Ma gli elementi sono di tipo generico T, quindi usare lo 0
// non ha senso... prova ad inventarti qualcosa :-)
}
Matrix33<T>(const Matrix33<T>& source)
{
// Un comodo costruttore che crea una matrice copiandone una esistente...
// Implica la presenza dell'operatore di assegnazione nel tipo generico T
m_11 = source.m_11;
m_12 = source.m_12;
// ...
// ...ecc...
}
// Generico distruttore
~Matrix<T>()
{
}
// Possibile implementazione di forward iterator:
// l'aritmetica dei puntatori ci fornirà "gratis" gli operatori di somma e differenza
typedef T* iterator;
typedef const T* const_iterator;
iterator begin() { return &m_elements[0]; }
iterator end() { return &m_SENTRY; }
// end() è un elemento SENTRY, non appartiene alla matrice! (vedi il costruttore...)
// Accesso agli elementi con bound checking! Figata!
iterator at(unsigned int x, unsigned int y)
{
if (x < 3 && y < 3)
return &m_2dmatrix[x][y];
else
return end();
}
iterator at(unsigned int t)
{
if (t < 9)
return &m_elements[t];
else
return end();
}
// Bisogna implementare anche gli operatori [], +, -, +=, ecc...
// ma questi li devi fare tu!
// ti faccio un semplice esempio:
Matrix33<T>& operator += (const Matrix33<T>& other)
{
// Il for non è la soluzione migliore... fai qualcosa di più ottimizzato qui!
for (unsigned int i=0; i<9; i++)
m_elements[i] += other.at(i);
// Questa parte è fondamentale perchè le espressioni del tipo "matrice1 = matrice2 = matrice3;" abbiano senso!
return *this;
}
// ...fine esempio
// Potresti scegliere di rendere "public" questa parte, ma perderesti il bound checking
// Usa questa possibilità con molta cautela...
private:
// La union ci consente di accedere agli stessi dati nel modo che preferiamo:
// Come matrice bidimensionale (m_2dmatrix)
// Come array (m_elements)
// Oppure singolarmente ai vari elementi (m_11, m_23, m_31, ecc...)
union
{
T m_elements[10]; // 10!? mmmh....
T m_2dmatrix[3][3];
struct
{
T m_11, m_12, m_13;
T m_21, m_22, m_23;
T m_31, m_32, m_33;
T m_SENTRY; // Toh! Un intruso... chissà a cosa serve...
};
};
};
|
Ovviamente se ti serve qualcosa di più generico dovresti prevedere anche matrici di dimensioni generiche X,Y. Le dimensioni dovrebbero essere passate direttamente nel costruttore e dovresti allocare dinamicamente la memoria delle sue celle.
Ma questo ha tanti svantaggi:
- In questo caso perderesti tutti gli operatori matematici (perchè due matrici si possono sommare, sottrarre, ecc... soltanto se hanno dimensioni identiche. Il prodotto righe per colonne si può fare solo se la prima ha un numero di righe uguale al numero di colonne della seconda, e così via per gli altri operatori.)
- Non potresti usare una union per rappresentare in modi diversi gli stessi dati (le union funzionano solo quando la loro dimensione è nota a compile-time)
- L'allocazione dinamica della memoria comporta ulteriore overhead, frammentazione della memoria e l'impossibilità di allocare matrici stack-only (cioè come variabili locali). Questo rallenta inoltre l'esecuzione di codice come: mat = miaMatrice + tuaMatrice * 0.5f; perchè a) devi aggiungere i check sulle dimensioni delle varie matrici e b) devi allocare la memoria nell'heap per contenere i risultati intermedi delle sottoespressioni.
spero di averti dato quello che cercavi... il codice che ti ho abbozzato non è stato testato, quindi potrebbe non compilare, crashare o addirittura esploderti in faccia. Usalo a tuo rischio e pericolo!
Ciao!
|
|
Poggi Marco (Member)
Guru
Messaggi: 969
Iscritto: 05/01/2010
|
Bravo TheKaneB! Ho imparato qualcosa di nuovo!!
|
|
TheKaneB (Member)
Guru^2
Messaggi: 1792
Iscritto: 26/06/2009
|
Postato originariamente da Poggi Marco:
Bravo TheKaneB! Ho imparato qualcosa di nuovo!! |
Grazie, mi fa molto piacere che il mio intervento si sia rivelato utile per qualcuno |
|
simoo88 (Normal User)
Newbie
Messaggi: 12
Iscritto: 15/05/2009
|
Grazie per le risposte.
Ho deciso di implementare la matrice vedendola come un semplice array,che avrà come variabili dim_x , dim_y e il puntatore.
Volevo chiedervi come deve essere implementato il costruttore di default.
Non riesco a trovare la soluzione migliore,secondo voi devo azzerare i valori delle mie variabili (ricordo che è una classe tamplate),nascondere il costruttore di default mettendolo privato in modo tale che non possa essere usato dal programmatore oppure definire delle variabili globali e quindi creare una matrice di default con dimensioni predefinite?
grazie.
|
|
TheKaneB (Member)
Guru^2
Messaggi: 1792
Iscritto: 26/06/2009
|
il costruttore non dovrebbe inizializzare la matrice.
Se l'utente di questa classe volesse inizializzare la matrice con valori diversi da zero, si avrebbe solo uno spreco di tempo.
Però volendo puoi prevedere un costruttore di inizializzazione al quale far passare un valore dall'utente...
ad esempio:
Codice sorgente - presumibilmente C++ |
template <typename T> Matrix::Matrix(const T& initValue, unsigned int width, unsigned int height) { m_sizeX = width; m_sizeY = height; m_numOfElements = m_xizeX * m_sizeY; m_elements = new T[m_numOfElements]; for (int i=0; i<m_numOfElements; i++) m_elements[i] = initValue; }
|
In questo modo eviti il problema dello "zero". Infatti il tipo generico T potrebbe essere ad esempio a sua volta un'altra classe (magari una classe che implementa polinomi, numeri complessi, quaternioni, stringhe e chi più ne ha più ne metta), quindi il valore numerico 0 non sarebbe valido come inizializzatore.
Invece il programmatore potrebbe usare la classe così:
Codice sorgente - presumibilmente Plain Text |
Matrix<String> temp(String(""), 4, 4);
//... ecc...
|
.. e avrebbe una matrice 4x4 di stringhe, il cui valore di init sarebbe la stringa vuota "". |
|