Newsgroup: it.comp.lang.c++
Titolo: Domanda

Ho una definizione di classe come questa:
class Node{
public:
  Node(int aKey):mKey(aKey),mNext(0),mPrev(0){}
public:
  int mKey;
  Node* mNext;
  Node* mPrev;
};Che significato ha la linea del costruttore?Mi sfugge il significato di 
quelle dichiarazioni.. non dovrebbero essere tra le graffe? 

   Node(int aKey):mKey(aKey),mNext(0),mPrev(0){}
Tutto  cioè che si trova dopo i due punti e prima della graffa aperta,
in C++ rappresenta la lista di inizializzazione.
La lista di inizializzazione viene eseguita PRIMA di tutto ciò che si 
trova nelle parentesi graffe ed è utilizzata SOLO per quelle classi che
derivano da altre classi per le quali si vuole utilizzare una 
inizializzazione diversa da quella di default (=costruttore di default), 
ammessa che quest' ultima siano presenti.
Ciò che si trova tra le graffe serve SOLO per inizializzare la classe 
stessa all'atto della creazione e non le classi da cui è derivata (per 
quelle c'è la lista di inizializzazione).

 ammessa che quest' ultima siano presenti.
Non e' vero. La lista di inizializzazione e' utilizzabile anche per 
inizializzare le variabili membro dell'oggetto stesso, e anche per 
classi che non derivano da altre classi.
Anzi, e' necessario usare la lista di inizializzazione per eseguire una 
vera e propria inizializzazione di tali variabili.
Si consideri la seguente classe, dove T e' un tipo:
   class C {
     T x1, x2;
   public:
     C(): x1(0) { x2 = 0; }
   }
nel costruttore viene eseguito un codice analogo al seguente:
   T x1(0);
   T x2;
   x2 = 0;
Nel caso che T sia un tipo non di classe, non c'e' differenza, ma se T 
e' una classe con uno o piu' costruttori, oppure con un operatore di 
assegnamento, le cose cambiano.
L'inizializzazione puo' essere piu' efficiente, o addiritture necessaria.
Infatti, se T e' un reference o un tipo const o una classe con un 
costruttore ma senza il costruttore di default, x1 e x2 devono essere 
inizializzati nella lista di inizializzazione.
-- 
     Carlo Milanesi
 
http://digilander.libero.it/carlmila

 vera e propria inizializzazione di tali variabili.
Certo che puoi utilizzarla per inizializzare le variabili della classe 
stessa...ma è superfluo visto che per quello puoi usare il corpo del 
costruttore! Il termine che ho usato SOLO, indica che è utili SOLO per
inizializzare le classi derivate.
Non ha senso inizializzare un oggetto della classe nella lista di 
inizializzazione, visto che la stessa può essere eseguita nel costruttore!
class C {
     T *a;
     C()
};
è più chiaro inizializzare T nel costruttore:
C::C()
{
    a = new T("pippo");
}
e superfluo farlo nella lista di inizializzazione.
Lista che è NECESSARIA per inizializzare le classi derivate
della classe che si sta istanziando!

 inizializzazione, visto che la stessa può essere eseguita nel costruttore!
Per la verità, il comportamento + corretto sarebbe l'esatto contrario,
cioè inizializzare le variabili nella lista di inizializzazione dei
membri e non nel corpo del costruttore. Difatti, secondo le specifiche,
anche se un membro di una classe non viene inizializzato esplicitamente
nella lista, il compilatore genera comunque una inizializzazione di
default. di conseguenza se io scrivo:
class C
{
int m;
C()
{
 m = 1;
}
};
il compilatore genera un costruttore che fa così:
C::C()
: m()
{
m=1;
}
Per i tipi built-in ovviamente + un'inezia. Ma per un tipo definito
dall'utente può essere pesante.
Ciao
Gianluca

 
No. I membri dati non possono essere *inizializzati* nel corpo del
costruttore, perché in quella fase l'inizializzazione di tutti i membri e
delle classi basi è già avvenuta. Puoi, naturalmente, assegnare loro dei
nuovi valori, ma si tratta, appunto, di un *assegnamento*, non di una
inizializzazione.
Max

 della classe che si sta istanziando!
Gli esperti danno il consiglio opposto. Scott Meyers ha un "item" su
questo argomento in "Effective C++", seconda edizione: "Item 12: Prefer
initialization to assignment in constructors".
Ciao,
Nicola

 costruttore, perché in quella fase l'inizializzazione di tutti i membri e
[cut]
Certo nel caso di oggetti non allocati dinamicamente è giusto...in 
quella fase verrebbe eseguito il costruttore di default dell'oggetto;
ma questo comportamento viene a cadere se vengono usati oggetti membro 
(praticamente sempre) allocati dinamicamente.

 default. di conseguenza se io scrivo:
Certo, ma lasciamo perdere quello che viene generato automaticamente dal 
compilatore, perchè non è detto che tutti lo facciano.

 initialization to assignment in constructors".
No scusa non mi è chiaro questo concetto!
Come fai ad inizializzare una classe base con un costruttore non di 
default, se non lo fai nella lista di inizializzazione?!?!
class C : public Z {
	C();
}
C::C()  // qui per default viene inizializzato Z()
{
}
C::C() : Z("pippo")  // qui viene istanziato usando un'altro costruttore
{
}
Come è possibile fare il contrario!?

 rara questa specificata qui da te
O_O
 Confusing Initialization and Assignment in Constructors
3' girone dell'inferno
-- 
sbi 

 
no...personalmente uso solo puntatori, che come ben sai non devono
essere inizializzati per forza come i reference!!

 della classe che si sta istanziando!
Ok, questo si!
Ciao,
Francesco.
-- 
Qual'e' il colmo per uno studente-lavoratore?
Avere un quadrato che si crede un rettangolo.

 
Beh diciamo che la colpa è mia...nel senso che non sono un programmatore
C++ "puro" (preferisco e utilizzo il C come si è potuto notare :-) ).
Però personalmente trovo più leggibile una sintassi interna al corpo del 
costruttore anzicchè nella lista di inizializzazione!
Però questo è un altro discorso...

 essere inizializzati per forza come i reference!!
class A{
  int* const thePointer_;
 public:
  A(int* const aPointer): thePointer_(aPointer){}
};
"burn"
-- 
sbi 

 "burn"
E cosa hai risolto!? ho parlato di puntatori standard
e non di puntatori costanti!
E' ovvio che un puntatore costante si comporta simile
ad un reference: cioè deve essere inizializzato!!!
Mi sembra più che tu lo faccia per gara...

 Mi sembra piů che tu lo faccia per gara...
.. esci da questo corpo!
-- 
sbi 

 
BBBRAVISSIMO!

 (praticamente sempre) allocati dinamicamente.
Bravo, cosi' l'exception safety se ne va a farsi benedire...
Ripeti cento volte: il C++ non e' Java, il C++ non e' Java...
Ciao,
Nicola

 default, se non lo fai nella lista di inizializzazione?!?!
No, la mia obiezione e' all'intero senso del tuo messaggio, nno a
quest'ultima frase. Ovvero intendo dire che e' *sempre* meglio usare la
lista di inizializzazione rispetto all'assegnamento dei membri dato nel
corpo del costruttore. Il titolo dell'Item 12 tradotto in italiano e'
"Preferisci l'inizializzazione all'assegnamento nei costruttori".
Ciao,
Nicola

 Però questo è un altro discorso...
Tutto dipende da come indenti il codice.
Certo che se scrivi
C::C() : a( new T("pippo") ), b( new T("pluto") ), c( new T("topolino") 
), d( new T("paperino") ), e( new T("minnie") ) {}
le cose si fanno difficili.
Però puoi scrivere
C:C() :
     a( new T("pippo") ),
     b( new T("pluto") ),
     c( new T("topolino") ),
     d( new T("paperino") ),
     e( new T("minnie") )
{
     // Wow, è già tutto inizializzato :)
}
Kiuhnm

 Ripeti cento volte: il C++ non e' Java, il C++ non e' Java...
Si ma lo puoi ripetere all'infinito...io non uso java ;-)

 };
Ahh ma il vostro è un vizio!!
E' ovvio che qui bisogna usare la lista...ma solo perchè la var i è const!!
Già qualcuno ha fatto lo stesso esempio, ma con un puntatore...

 }
Eh si...
beh vuol dire che devo incominciare ad a diventare più
un programmatore C++ :-(
:-)

 Si ma lo puoi ripetere all'infinito...io non uso java ;-)
Da quello che scrivi lo si sarebbe pensato, ma ho poi letto che usi
prevalentemente il C.
Ciao,
Nicola
(1) A meno che non sia utilizzata esplicitamente la versione che non
usa eccezioni, con istruzioni tipo
p = new (std::nothrow) P;

 :-)
Noialtri siamo qui per questo. Che tu lo voglia o no ;-)
Ciao,
Nicola

 Già qualcuno ha fatto lo stesso esempio, ma con un puntatore...
E' solo per dimostrarti che il poter rinviare l'attribuzione di un
valore iniziale sensato all'assegnamento all'interno del corpo del
costruttore non e' solo potenzialmente meno efficiente, ma e' anche
meno generale di quanto non pensi.
In pratica ha senso solo se il valore di uno dei membri dato dipende
dalla combinazione dei valori di altri membri dato. Ed in questo caso
conviene fermarsi a pensare un istante se non sia meglio portare i
membri dato indipendenti in una classe base, magari privata.
Ciao,
Nicola

 std::bad_alloc. Se hai una classe come:
Ahh ok, capisco cosa vuoi dire!
Però lo stesso effetto con new,
lo puoi fare con un try...catch...finally
e risolvi il problema dell'eventuale eccezione.

 Noialtri siamo qui per questo. Che tu lo voglia o no ;-)
noo a me fa piacere che ci siate...
cosě si ha uno scambio di idee e miglioramenti vari!!


Intanto il C++ standard il finally non ce l'ha. In secondo luogo il
codice viene decisamente piu' contorto.
Ciao,
Nicola


Tra l'altro, programmando da un po', mi capita spesso di assumere per vere
alcune cose in modo un po' troppo "assiomatico", e sono proprio le
discussioni che partono dalle domande piu' semplici a mettere in luce
questi miei errori... :-)
Ciao.


 e risolvi il problema dell'eventuale eccezione.
In C++ ci sono solo *try* e *catch*. Il *finally* c'e' solo in Java...
Hi hi... Forse hai programmato in Java in una vita passata... :-P


 
Lo so che finally non è standard, ma questo non vuol dire che
non è implementata in molti compilatori (vedi: C++Builder, VisualC++, 
ecc..); non solo in java!
In realtà ho anche programmato con java...

 invece usi ad esempio
[...]
Quello che dici è giusto, ma non ha niente a che vedere con la lista 
d'inizializzazione.
Hai solo sostituito oggetti insicuri con oggetti sicuri.
Quando non obbligatoria, la lista d'inizializzazione è semplicemente più 
efficiente. Non è affatto più sicura.
Kiuhnm

 In realtà ho anche programmato con java...
Dai, ma io scherzavo. Mi stavo riferendo un po' ai post di Roberto... :-)



In realta' mi riferivo all'affermazione di AlterX che i membri dato
sono "quasi sempre" puntatori. Questa affermazione combinata con
l'abitudine di assegnare ai membri dato nel corpo del costruttore porta
all'esempio del mio post.
L'esempio avrebbe funzionato lo stesso con l'"accrescimento" dei vector
posto nel corpo del costruttore.
Ciao,
Nicola

 }
Non e' esatto. Se un membro di una classe non viene inizializzato nella 
lista di inizializzazione, il compilatore genera lo stesso codice che 
genera per una dichiarazione di variabile sullo stack, cioe' se il tipo 
della variabile e' una classe con un costruttore di default, viene 
chiamato tale costruttore, altrimenti la variabile non viene 
inizializzata, e quindi il suo valore e' indefinito.
Per esempio, il seguente programma:
#include <iostream>
class C {
   int x, y, z;
public:
   C(): x(), y(12) {
     std::cout << x << " " << y << " " << z << " ";
     x = 23; y = 34; z = 45;
     std::cout << x << " " << y << " " << z;
   }
};
int main() { C c; }
puo' avere il seguente output (realmente ottenuto con un compilatore),
0 12 4263680 23 34 45
oppure il seguente (realmente ottenuto con un altro compilatore):
0 12 2289592 23 34 45
-- 
     Carlo Milanesi
 
http://digilander.libero.it/carlmila

 inizializzata, e quindi il suo valore e' indefinito.
Hai ragione. Ero stato fuorviato dalla lettura di "Accelerated C++"
Ciao
Gianluca