Newsgroup: it.comp.lang.c++
Titolo: Costruttore di default

Ho creato due classi identiche, tranne per il fatto che in una delle
due ho esplicitato il costruttore di default (vuoto). Poi ho misurato
il tempo necessario ad allocare sullo stack un array di oggetti del
primo tipo e un array di oggetti del secondo tipo. E qui arriva il
problema: compilando sotto Windows con BCC55, per allocare gli oggetti
con costruttore di default esplicitato viene impiegato un tempo
enormemente superiore rispetto agli oggetti con costruttore di default
implicito (ho provato a settare ogni tipo di ottimizzazioni nel
compilatore ma il risultato non cambia). Come mai? E' un problema del
compilatore? In caso contrario, il costruttore creato implicitamente
in cosa differisce da quello "manuale"?
Per chi è interessato, questo è il "benchmark" che ho utilizzato:
#include<windows.h>
#include<iostream>
using namespace std;
class Person1 {
public:
    int age;
    int height;
};
class Person2 {
public:
    Person2() { }
    int age;
    int height;
};
int main() {
    LARGE_INTEGER before;
    LARGE_INTEGER after;
    QueryPerformanceCounter(&before);
    Person1 p1[1000];	// prima allocazione sullo stack
    QueryPerformanceCounter(&after);
    cout << "Person1: " << after.QuadPart - before.QuadPart << '\n';
    QueryPerformanceCounter(&before);
    Person2 p2[1000];	// seconda allocazione sullo stack
    QueryPerformanceCounter(&after);
    cout << "Person2: " << after.QuadPart - before.QuadPart << '\n';
}
Grazie anticipatamente per le risposte.

 Per chi è interessato, questo è il "benchmark" che ho utilizzato:
[cut]
ho compilato il programma con VC++6, e sul mio PIII733 ho provato
queste 2 configurazioni:
Ottimizzazione: "Disable (Debug)"
Person 1: 7
Person 2: 96
Ottimizzazione: "Maximize speed"
Person 1: 6
Person 2: 6
se ottimizzi non c'è nessuna differenza tra i due. se non ottimizzi
viene chiamato il costruttore (vuoto), quindi vengono eseguite più
operazioni rispetto a quella senza.
credo che qua l'ottimizzazione si limita ad eliminare la chiamata al
costruttore che hai definito tu, e chiama quello vuoto di default,
perchè basta mettere una qualsiasi operazione all'interno del
costruttore, e il tempo impiegato ad allocare le classi aumenta.
ciao
csaero:)

 in cosa differisce da quello "manuale"?
Questo succede perche' il tuo costruttore apparentemente vuoto in
realta' forza l'inizializzazione a zero dei due membri. In realta' il
compilatore avrebbe dovuto accorgersi che sia p1 che p2 possono essere
spostate fuori dallo stack ed inizializzate staticamente. Puoi vedere
cosa succede con una versione modificata del tuo esempio, che allego.
Ciao,
Nicola
#include<windows.h>
#include<iostream>
using namespace std;
class Person1 {
public:
    int age;
    int height;
};
class Person2 {
public:
    Person2() { }
    int age;
    int height;
};
// Questa serve a sporcare la memoria dove finiranno p1 e p2.
// Il ciclo di cout impedisce al compilatore di ottimizzare
// via la chiamata
void fun() {
    int a[2000];
    for ( int i = 0; i < 2000; ++i )
    	a[i] = i;
    for ( int i = 0; i < 2000; ++i )
    	cout << a[i];
}
void fun2() {
    LARGE_INTEGER before;
    LARGE_INTEGER after;
    QueryPerformanceCounter(&before);
    Person1 p1[1024];   // prima allocazione sullo stack
    QueryPerformanceCounter(&after);
    cout << "Person1: " << ( after.QuadPart - before.QuadPart ) << '\n';
    // Questi contengono i valori impostati precedentemente
    cout << "p1[100]: " << p1[100].age << "	" << p1[100].height << '\n';
    QueryPerformanceCounter(&before);
    Person2 p2[1024];   // seconda allocazione sullo stack
    QueryPerformanceCounter(&after);
    cout << "Person2: " << ( after.QuadPart - before.QuadPart ) << '\n';
    // Questi sono zero
    cout << "p2[100]: " << p2[100].age << "	" << p2[100].height << '\n';
}
int main() {
    fun();
    fun2();
}

spostate fuori dallo stack ed inizializzate staticamente.
Non è detto: io voglio solo che il compilatore mi sposti lo stack
pointer della dimensione del vettore e non si metta a fare cose strane
di nascosto (per ragioni di efficienza). Poi ci penso io a riempire
gli array con quello che mi serve.
Mah, sono rimasto molto deluso dal BCC55!

operazioni rispetto a quella senza.
Quali istruzioni in piů? Ammettendo che il compilatore sia
completamente stupido (non ci vuole mica tanto a capire se un
costruttore č vuoto), potrebbe generare un ciclo che esegue il
costruttore (inline) per ogni elemento. Ma questo ciclo sarebbe un
ciclo "vuoto", quindi nelle varie ottimizzazioni standard dopo la
generazione del codice verrebbe tolto. Al limite, potrei anche
tollerare che venga lasciato il ciclo vuoto, ma il BCC55 addirittura
si inventa una inizializzazione a zero!
Mah...

cosa succede con una versione modificata del tuo esempio, che allego.
Ho provato il programma con BCC55 e il comportamento sembra in
effetti essere quello che affermi tu. Dico sembra perche' se nel
corpo delle funzion scambi le due chiamate (prima cioe' costruisci
Persona2 e poi Persona1) comunque e' la seconda chiamata che si
trova i valori inizializzati a zero.
Nei documenti non ufficiali in mio possesso viene detto inoltre
esplicitamente che:
  12.1 / 7 [class.ctor]
 
  ...
  The implicitly-defined default constructor performs the set
  of initializations of the class that would be performed by
  a user-written default constructor for that class with an
  empty mem-initializer-list (_class.base.init_) and an
  empty function body.
  ...
Questo secondo me significa che aggiungere un costruttore vuoto
come e' stato fatto nella classe persona non deve cambiare il
comportamento.
Un altro punto in cui il concetto viene ribadito e':
  12.6.2 / 4 [class.base.init]
  If a given nonstatic data member or base class is not
  named by a mem-initializer-id (including the case where
  there is no mem-initializer-list because the constructor
  has no ctor-initializer), then
  --If the entity is a nonstatic data member of (possibly
    cv-qualified) class type (or array thereof) or a base
    class, and the entity  class is a non-POD class, the
    entity is default-initialized (_dcl.init_).
    If the entity is a nonstatic data member of a
    const-qualified  type, the entity class shall have a
    user-declared default constructor.
  --Otherwise, the entity is not initialized. If the
    entity is of const-qualified type or reference type,
    or of a (possibly  cv-qualified)  POD  class  type
    (or array thereof) containing (directly or indirectly)
    a member of a const-qualified type, the program is
    ill-formed.
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

 gli array con quello che mi serve.
Ho l'impressione che il compilatore non implementi la funzione inline e che non
ottimizzi quindi la chiamata.
Hai provato a usare il debugger in modalità assembler per vedere il codice
effettivamente generato?
--
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556

effettivamente generato?
Ho provato a fargli generare il codice ASM intermedio, e in effetti si
vede che mentre il costruttore generato automaticamente non appare da
nessuna parte, quello vuoto che ho scritto io compare con una serie di
istruzioni bizzarre (tra cui una call che sembra avere a che fare con
le eccezioni... eccezioni in un costruttore vuoto?!)
Boh!

spostate fuori dallo stack ed inizializzate staticamente
Beh, non mi sembra comunque un comportamento molto corretto da parte
del BCC55(ammesso che sia l'unico compilatore a comportarsi in tal
modo)e credo che non sia neanche standard, anche se posso capire che
non capita spesso di definire costruttori vuoti.
Il Ghenio
-----
ZIO : Fun and games are over with. Anyone who dares
get in my way shall perish.
DIO ci salvi dagli ocx del Viral Basic.
-----

 trova i valori inizializzati a zero.
In effetti mi sbagliavo. Sono stato fuorviato dal fatto che in una
funzione del tipo:
void f() {
{ Person1 p1; }
{ Person2 p2; }
}
p1 e p2 *non* sono allocate nella stessa zona di memoria. In realta' il
compilatore non inizializza niente, ma genera codice per chiamare il
costruttore vuoto di Person2 (e non si capisce perche').
[altre osservazioni corrette omesse]
Ciao,
Nicola