Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
C/C++ - Classe generica Matrici
Forum - C/C++ - Classe generica Matrici

Pagine: [ 1 2 ] Precedente | Prossimo
Avatar
simoo88 (Normal User)
Newbie


Messaggi: 12
Iscritto: 15/05/2009

Segnala al moderatore
Postato alle 22:02
Venerdì, 22/01/2010
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.:asd:

PM Quote
Avatar
simoo88 (Normal User)
Newbie


Messaggi: 12
Iscritto: 15/05/2009

Segnala al moderatore
Postato alle 22:05
Venerdì, 22/01/2010
ah ricordo che non posso usare librerie esterne e strutture dati container della std library come
std::vector, std::list e simili.

PM Quote
Avatar
Poggi Marco (Member)
Guru


Messaggi: 969
Iscritto: 05/01/2010

Segnala al moderatore
Postato alle 22:11
Venerdì, 22/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
PM Quote
Avatar
lorenzo (Normal User)
Guru


Messaggi: 1178
Iscritto: 15/04/2008

Segnala al moderatore
Postato alle 23:18
Venerdì, 22/01/2010
ah, i progetti universitari.....comunque se dobbiamo aiutarti dovresti postare il codice che hai già scritto ;)

PM Quote
Avatar
TheKaneB (Member)
Guru^2


Messaggi: 1792
Iscritto: 26/06/2009

Segnala al moderatore
Postato alle 10:15
Sabato, 23/01/2010
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++

  1. // il file è matrix.h, ricordati che i template non vanno compilati come i .cpp!
  2.  
  3. template <typename T>
  4. class Matrix33
  5. {
  6. public:
  7.     Matrix33<T>()
  8.     {
  9.         // Qui potresti inserire del codice per inizializzare la matrice
  10.         // Ma gli elementi sono di tipo generico T, quindi usare lo 0
  11.         // non ha senso... prova ad inventarti qualcosa :-)
  12.     }
  13.  
  14.     Matrix33<T>(const Matrix33<T>& source)
  15.     {
  16.         // Un comodo costruttore che crea una matrice copiandone una esistente...
  17.         // Implica la presenza dell'operatore di assegnazione nel tipo generico T
  18.         m_11 = source.m_11;
  19.         m_12 = source.m_12;
  20.         // ...
  21.         // ...ecc...
  22.     }
  23.  
  24.     // Generico distruttore
  25.     ~Matrix<T>()
  26.     {
  27.     }
  28.  
  29.     // Possibile implementazione di forward iterator:
  30.     // l'aritmetica dei puntatori ci fornirà "gratis" gli operatori di somma e differenza
  31.     typedef T* iterator;
  32.     typedef const T* const_iterator;
  33.    
  34.     iterator begin() { return &m_elements[0]; }
  35.     iterator end()   { return &m_SENTRY; }
  36.     // end() è un elemento SENTRY, non appartiene alla matrice! (vedi il costruttore...)
  37.    
  38.     // Accesso agli elementi con bound checking! Figata!
  39.     iterator at(unsigned int x, unsigned int y)
  40.     {
  41.       if (x < 3 && y < 3)
  42.         return &m_2dmatrix[x][y];
  43.       else
  44.         return end();
  45.     }
  46.    
  47.     iterator at(unsigned int t)
  48.     {
  49.       if (t < 9)
  50.         return &m_elements[t];
  51.       else
  52.         return end();
  53.     }
  54.    
  55.     // Bisogna implementare anche gli operatori [], +, -, +=, ecc...
  56.     // ma questi li devi fare tu!
  57.     // ti faccio un semplice esempio:
  58.     Matrix33<T>& operator += (const Matrix33<T>& other)
  59.     {
  60.       // Il for non è la soluzione migliore... fai qualcosa di più ottimizzato qui!
  61.       for (unsigned int i=0; i<9; i++)
  62.         m_elements[i] += other.at(i);
  63.      
  64.       // Questa parte è fondamentale perchè le espressioni del tipo "matrice1 = matrice2 = matrice3;" abbiano senso!
  65.       return *this;
  66.     }
  67.     // ...fine esempio
  68.  
  69. // Potresti scegliere di rendere "public" questa parte, ma perderesti il bound checking
  70. // Usa questa possibilità con molta cautela...
  71. private:
  72.     // La union ci consente di accedere agli stessi dati nel modo che preferiamo:
  73.     // Come matrice bidimensionale (m_2dmatrix)
  74.     // Come array (m_elements)
  75.     // Oppure singolarmente ai vari elementi (m_11, m_23, m_31, ecc...)
  76.     union
  77.     {
  78.         T m_elements[10]; // 10!? mmmh....
  79.         T m_2dmatrix[3][3];
  80.         struct
  81.         {
  82.           T m_11, m_12, m_13;
  83.           T m_21, m_22, m_23;
  84.           T m_31, m_32, m_33;
  85.           T m_SENTRY; // Toh! Un intruso... chissà a cosa serve...
  86.         };
  87.     };
  88. };



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!

PM Quote
Avatar
Poggi Marco (Member)
Guru


Messaggi: 969
Iscritto: 05/01/2010

Segnala al moderatore
Postato alle 10:35
Sabato, 23/01/2010
Bravo TheKaneB! Ho imparato qualcosa di nuovo!!

PM Quote
Avatar
TheKaneB (Member)
Guru^2


Messaggi: 1792
Iscritto: 26/06/2009

Segnala al moderatore
Postato alle 11:12
Sabato, 23/01/2010
Testo quotato

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

PM Quote
Avatar
simoo88 (Normal User)
Newbie


Messaggi: 12
Iscritto: 15/05/2009

Segnala al moderatore
Postato alle 10:34
Sabato, 30/01/2010
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.

PM Quote
Avatar
TheKaneB (Member)
Guru^2


Messaggi: 1792
Iscritto: 26/06/2009

Segnala al moderatore
Postato alle 14:34
Sabato, 30/01/2010
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++

  1. template <typename T>
  2. Matrix::Matrix(const T& initValue, unsigned int width, unsigned int height)
  3. {
  4.   m_sizeX = width;
  5.   m_sizeY = height;
  6.  
  7.   m_numOfElements = m_xizeX * m_sizeY;
  8.  
  9.   m_elements = new T[m_numOfElements];
  10.  
  11.   for (int i=0; i<m_numOfElements; i++)
  12.     m_elements[i] = initValue;
  13. }



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

  1. Matrix<String> temp(String(""), 4, 4);
  2. //... ecc...


.. e avrebbe una matrice 4x4 di stringhe, il cui valore di init sarebbe la stringa vuota "".

PM Quote
Pagine: [ 1 2 ] Precedente | Prossimo