Newsgroup: it.comp.lang.c++
Titolo: Puntatore a matrice di puntatori: aiuto su sintassi e semantica.

Salve, qualcuno di voi potrebbe dirmi che sintassi usare nel seguente
codice:
Le richieste sono commentate accanto alla riga relativa
#include <iostream>
class Z {
 public :
 Z() {std::cout << "Z()" << std::endl;};
 ~Z() {std::cout << "~Z()" << std::endl;}
};
int main() {
 Z z;
 Z (* p) [2][3]; // Puntatore ad una matrice di Z
 p = new Z[0][2][3]; // Perche' non chiama Z::Z() ? Non c'e' una sintassi
migliore?
 (*p)[0][0] = z; // Assegnamento ok, ma se non chiamo new da errore, quindi
il new costruisce qualcosa. Ma non chiama Z::Z()
 delete p; // Dovrebbe chiamare Z::~Z() ma non lo fa.
 Z* (* pp) [2][3]; // Puntatore ad una matrice di puntatori a Z
 pp = new ... // Che sintassi devo usare?
 *(*pp)[0][0] = z; // Errore a run-time per l'assenza di new
 delete pp; // Come il delete sopra */
 return 0;
}
Praticamente e' il main che chiama che i costruttori e i distruttori non new
e delete. Ma se new non lo uso gli assegnamenti danno errore a run-time.
Grazie

 [CUT]
Ciao,
Questa è una FAQ e ci sono tre esempi alla sezione '8' del documento sulle
FAQ ufficiali del NG.
Comunque ti riporto una modifica più compatta (ci sono parecchi cicli for in
meno) rispetto al primo esempio dei tre suddetti e che include anche
l'istanza di un puntatore alla matrice richiesta in qualche modo nel codice
da te scritto:
Tra l'altro essendo una matrice quadrata non si capisce bene se si
creano/distruggono prima le righe o le colonne o viceversa. Poi ho preferito
assegnare dei numeri crescenti da 0 a rows*cols-1 in modo da potere meglio
controllare i singoli elementi. Spero di avere capito cosa ti serve e di non
avere fatto troppi errori.
#include <iostream>
class Z {
 public :
 Z() {std::cout << "Z()" << std::endl;};
 ~Z() {std::cout << "~Z()" << std::endl;}
 int num;
};
int main()
{
    const int rows = 3;
    const int cols = 4;
    Z** matrix = 0; // Puntatore ad una matrice di Z
 
    matrix = new Z*[rows]; // prima dimensione
    for( int i=0; i<rows; ++i )
    {
        matrix[i] = new Z[cols]; // seconda dimensione
        for( int j=0; j<cols; ++j )
            matrix[i][j].num = j + i * cols;
    }
 
    // verifica dell'assegnamento dei valori da 0 a rows*cols-1
    for( int i=0; i<rows; ++i )
        for( int j=0; j<cols; ++j )
            std::cout << matrix[i][j].num << (j == cols-1 ? '\n' : '\t');
 
    Z ***pp = &matrix; // Puntatore ad una matrice di puntatori a Z
    (*pp)[0][0].num = 999;
    std::cout << (*pp)[0][0].num << '\n'; // verifica dell'assegnamento
 
    for( int i=0; i<rows; ++i ) // distruzione matrici per riga
        delete[] matrix[i];
    delete[] matrix;
 
    return 0;
}
Saluti,
fabio de francesco
(fabiomdf)
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

 da te scritto:
Umh... si ok ma a me interessava capire perche' quel new non chiamava il
costruttore e i delete il distruttore ma piuttosto fa tutto il main. Inoltre
mi piacerebbe sapere la sintassi da utilizzare per new in maniera analoga a
come ho fatto per puntatori a matrici.
Ciao


Ciao,
Ho cercato di realizzare nel modo più semplice possibile quanto da te
chiesto tramite i commenti e speravo che si deducessero i motivi per cui il
tuo programma non può fare ciò che chiedi. Lo riporto per come l'hai scritto
così lo analiziamo riga per riga. Riporto anche i tuoi commenti e per ogni
riga successiva i miei (in stile C => /*...*/):
#include <iostream>
class Z {
 public :
 Z() {std::cout << "Z()" << std::endl;};
 ~Z() {std::cout << "~Z()" << std::endl;}
};
int main() {
 Z z;
/*
 la precedente istanza è irrilevante ai fini della costruzione della matrice.
 cmq è ok. esegue il costruttore una volta e il distruttore a fine main().
*/
 Z (* p) [2][3]; // Puntatore ad una matrice di Z
 p = new Z[0][2][3]; // Perche' non chiama Z::Z() ? Non c'e' una sintassi
migliore?
 (*p)[0][0] = z; // Assegnamento ok, ma se non chiamo new da errore, quindi
il new costruisce qualcosa. Ma non chiama Z::Z()
/*
 è corretto che non chiami il costruttore come invece ti aspetti perchè stai
allocando spazio per un
 puntatore a puntatori a Z e dopo stai facendo un assegnamento che invece non
è ok come pensi
 prova a stampare (*p)[0][0] con un debugger e vedi che viene fuori -> niente
di sensato
*/
 delete p; // Dovrebbe chiamare Z::~Z() ma non lo fa.
/*
no non deve! se tieni conto del precedente mio commento e fai anche girare
il programma che ti ho postato vedi che quando esegue "matrix = new
Z*[rows];" non è richiamato nessun costruttore perchè 'new' sta solo
costruendo spazio per puntatori e non per oggetti 'Z'.
 */
 Z* (* pp) [2][3]; // Puntatore ad una matrice di puntatori a Z
 pp = new ... // Che sintassi devo usare?
 *(*pp)[0][0] = z; // Errore a run-time per l'assenza di new
 delete pp; // Come il delete sopra
 /*
tutta la roba precedente non serve. se vuoi un puntatore ad una matrice ben
costruita devi fare qualcosa di simile a quanto ti ho scritto nel programma:
"Z ***pp = &matrix; // Puntatore ad una matrice di puntatori a Z" e poi lo
puoi usare come segue nello stesso programma alle righe seguenti. Quindi
prima ti costruisci la matrice e poi ne estrai l'indirizzo. Va bene... si
può imporre che la matrice sia ad un indirizzo predeterminato ma è un altro
discorso e non credo sia il punto della discussione
 */
 return 0;
}
 
Spero che questa volta la risposta sia stata più esauriente.
Buon lavoro,


 tutta la roba precedente non serve.
Ok, sono soltanto dei tentativi che faccio, cosi per dilettarmi nella
scrittura di codice incomprensibile :P
Ciao

  pp = new ... // Che sintassi devo usare?
   pp = new Z*[1][2][3];
   pp[0][0][0] = new Z;
   delete pp[0][0][0];
   delete [] pp; 
Piu semplice per una matrice:
  Z (*p) [3]; 
 
  p = new Z[2][3]; // chiama Z::Z() 6 volte
  p[0][0] = z; // chiama Z& operator=(const Z&)
  delete [] p; //chiama Z::~Z() 6 volte
  Z* (*pp) [3];
  pp = new Z*[2][3];
  pp[0][0] = &z; // pp[0][0] punta a z
  delete [] pp;
  pp = new Z*[2][3];
  pp[0][0] = new Z; // chiama Z::Z()
  ...
  *pp[0][0] = z; // chiama Z& Z::operator=(const Z&)
  ...
  delete pp[0][0]; // chiama Z::~Z()
  ...
  delete [] pp;
-- 
Horst

    pp[0][0][0] = new Z;
Ok, in effetti in questo caso bisogna che new restituisca un puntatore. E
poi si costruisce ogni oggetto singolarmente (con cicli for annidati
magari).
Grazie

 E p punta alla prima di queste n?
Si. E p+n-1 punta alla ultima ;-)
-- 
Horst


Ciao,
Bei modi compatti per la creazione di matrici! Complimenti. Quindi Noixe
faceva bene ad insistere su questi costrutti. Ci è voluto un po' per
digerirli e mi chiedo ancora cosa ne ho capito. Puoi rispodere, please, alle
domande dentro ai commenti?
1)
    Z (*p) [2][3]; // p è un puntatore ad una matrice 2x3 di Z?
    p = new Z[1][2][3];
    delete[] p;
2)
    Z (*pp) [3]; // pp è un puntatore ad un array di 3 Z?
    pp = new Z[2][3]; // se lo è non capisco questo assegnamento...
    delete [] pp;
3)
    Z* (*ppp) [3]; // è ppp un puntatore ad un array di 3 puntatori a Z?
    ppp = new Z*[2][3]; // purtroppo anche qui non ne comprendo l'assegnamento
    delete [] ppp;
 
Sono abituato a pensare prima in termini di righe e poi di colonne, quindi
non mi è chiaro come la dichiarazione dei vari puntatori in esempio possa
non tenere conto della prima dimensione che poi è fissata al momento della
'new'.
Ti ringrazio in anticipo per le eventuali risposte. Ah, dimenticavo... Non
sarà il caso di introdurre anche questo metodo nelle FAQ (dove si discute
dell'allocazione dinamica di matrici, al punto 8), visto che è il più
compatto?
Saluti,


 'new'.
Io credo che il punto sia il seguente: quando dichiari p, pp, ppp, essendo
dei puntatori, la dimensione non ha importanza, la si puo' considerare dopo,
proprio perche' in C/C++ c'e' una stretta relazione tra array e puntatori.
Spero di non aver fatto errori.
Ciao

 compatto?
Vedete un po' se va bene questa che così la aggiungo alla FAQ:
Approccio compatto:
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
  // Alloca la matrice
  int (*Matrice)[10] = new int [10][10];
  // Inizializza la matrice
  for(int i=0; i<10; ++i)
    for(int j=0; j<10; ++j)
      Matrice[i][j] = (i+1) * (j+1);
  // Visualizza il valore dei vari elementi
  for(int i=0; i<10; ++i) {
    for(int j=0; j<10; ++j)
      std::cout << setw(3) << Matrice[i][j] << " ";
     std::cout << std::endl;
   }
  // Dealloca la matrice.
  delete[] Matrice;
}
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg

 In questo caso, ppp e' un puntatore a *puntatori* di Z[3].
No, ppp è un puntatore ad array di 3 puntatori a Z. Se fosse un
puntatore a puntatore a Z[3] non potresti assegnargli il valore
restituito da new che è di tipo puntatore a Z*[3] (puntatore ad array da
3 puntatori a Z).


 3 puntatori a Z).
Mi sono espresso male: non intendevo puntatore a puntatore tipo T** ma
puntatore a puntatori inteso come puntatore ad un array di puntatori :)
Ciao

 Approccio compatto:
Forse sarebbe meglio se invece di int si usasse un tipo definito dall'utente
in modo da mostrare meglio come avvengono le chiamate al costruttore e al
distruttore?
Ciao

 distruttore?
Questo però romperebbe l'omogeneità del resto degli esempi e a me non va
proprio di rifarli tutti. Accontentati. :-)




Ciao,
Scusa per il ritardo, ho avuto un po' da fare... Grazie per le tue risposte.
In merito all'esempio da includere tra le FAQ avrei solo tre piccole
richieste da farti mentre per il resto mi sembra che vada tutto bene. Si
tratta di 'cosine' che potresti modificare in un minuto se vuoi:
1) Puoi modificare le impostazioni di numero righe e colonne in modo da
costruire una matrice rettangolare (ad esempio 5x8)? Te lo chiedo perchè,
per i 'principianti' che volessero capirne la costruzione, risulterebbe più
facile capire quando stai operando sulle righe piuttosto che sulle colonne.
Così si capirebbe meglio l'ordine in cui i 'for' sono eseguiti. Se sei
d'accordo ti proporrei anche di modificare il primo dei tre esempi già in
FAQ allo stesso modo, anche pechè li c'è la distruzione esplicita con
'delete' all'interno di un 'for' e potrebbe non essere chiaro cosa stai
eliminando. Spero di essere stato chiaro...
2) Ti propongo anche di evitare, per amore di buona tecnica di
programmazione, i 'magic numbers' relativi al numero di righe e colonne,
sostituendoli con dei "const int row = 5;" e "const int cols = 8;".
3) In ultimo modificherei anche gli assegnamenti ai singoli elementi da
"Matrice[i][j] = (i+1) * (j+1);" a "Matrice[i][j] = j + i * cols;", in modo
da ottenere una numerazione che parte da '0' e arriva a rows*cols-1 (cioè
righe*colonne-1). Serve perchè, in caso di stampa di questi valori da parte
di qualcuno che vuole capire il funzionamento, si ottengono i numeri in
sequenza ed è più facile capire se l'algoritmo è implementato bene.
Grazie ancora di tutto e buon lavoro,


 righe*colonne-1).
Fatto.
http://www.emagsoftware.it/faqcpp.txt
