Newsgroup: it.comp.lang.c++
Titolo: I misteri del C++

Problema n° 1.
Considerate il programma seguente:
class C_misteries
{
};
main()
{
C_misteries c;
}
Mi aspetterei che la definizione della variabile c sia subordinata
all'esistenza di un costruttore della classe, ma evidentemente ciò non è
vero visto che il compilatore non da alcun errore. E fin qui, non c'è
problema, ma se il codice diventa così:
class C_misteries
{
C_misteries(int){}
};
main()
{
C_misteries c;
}
Improvvisamente il compilatore genera un errore dicendo che non può trovare
il metodo "C_misteries::C_misteries()" ! Ma perchè, prima lo trovava? Come
se non bastasse dà come funzioni candidate la C_misteries(int) e una
fantomatica C_misteries(const C_misteries &). Che "const C_misteries &" sia
il puntatore implicito "this"? Ma in tal caso la "C_misteries()"
esisterebbe! Non mi raccapezzo...
Problema n° 2.
Altro programmino:
main()
{
const int a=8;
int *b=&a;
(*b)++;
cout<< a << *b;
}
Ovviamente il compilatore genera un warning in fase di compilazione per via
dell'assegnazione di uno *const  int a uno * int. Tuttavia l'istruzione
"(*b)++;" non crea problemi e incrementa l'oggetto puntato. Ebbene, l'output
del programma una volta lanciato è questo:89
Qualche spiegazione plausibile?
Il compilatore usato è il g++ 2.7equalcosa sotto Linux, mi farebbe piacere
sapere se su altri succedono le stesse cose. Grazie in anticipo,
_
Francesco "SiSkO" Napolitano
"Ma, Homer, questo è elemosinare!"
"No... è TELE-elemosinare!"

Non mi sembrano misteri.
--
Alessandro Monti

 Qualche spiegazione plausibile?
Certo.  "a" e` const (l'hai dichiarata cosi`) quindi il
compilatore ha tutti i diritti di ottimizzare a tempo di
compilazione sostituendola con la costante 8; non e`
tenuto a ricaricare il valore a runtime per vedere se
pur essendo dichiarato const e` stato cambiato.
b, una volta che te l'ha lasciato assegnare sia pure
con warning, deve ovviamente essere riletto runtime.
Alex

Grazie, mi sei stato di grande aiuto.
Alessandro Monti ha scritto nel messaggio ...


> Grazie, mi sei stato di grande aiuto.
Non capisco perchč non sono apprezzato.
Forse una risposta del tipo: "č proprio cosě che č stato definito il C++,
studiatelo" sarebbe stata meglio?
Forse non ho capito bene cosa volevi, a me sembrava che ti fossi accorto di
qualcosa che non sapevi ma che avevi pure capito il perchč.
Ti riporto alcune tue frasi:
"Mi aspetterei che la definizione della variabile c sia subordinata
all'esistenza di un costruttore della classe, ma evidentemente ciň non č
vero visto che il compilatore non da alcun errore."
Esatto, ti sei accorto che non č vero.
Il codice seguente ti dice invece che se fornisci tu un costruttore, non ne
crea di default da solo.
--
Alessandro Monti

compilazione sostituendola con la costante 8; non e`
E' qui che non capisco. Se il compilatore, in fase di compilazione,
sostituisce il const con la costante 8, cosa va ad incrementare "(*a)++"?
Devo supporre che ci sia da un lato una locazione di memoria che contiene un
valore modificabile a runtime e contemporaneamente una costante 8 scritta
altrove, ovvero 2 copie dello stesso dato. E mi viene da pensare che ci
siano due copie dello stesso dato per ogni const che definisco, una puntata
dal mio "int *a" e un'altra non sovrascrivibile con un *diverso* indirizzo
di memoria. Ma sicuramente mi sfugge qualcosa.
Lo so che si tratta di dettagli implementativi, ma mi sarebbe di grande
aiuto capirne comunque il meccanismo (per la serie "non mi rispondete "segui
lo standard e non c'hai problemi"":).
Grazie ancora,


crea di default da solo.
Già ,peccato che 1) Esiste un passo tra la conclusione che hai dato tu e la
presunta contraddizione che ho posto io, un passo che ignoravo (e che mi è
già stato spiegato in un altro messaggio) e che, per quanto ti possa
sembrare banale, non è scontato per chi cerca di formarsi delle basi solide
(leggi "niente dubbi") prima di continuare con lo studio di una materia
piuttosto complessa.
2) Avevo anche esposto un secondo problema.
Probabilmente non ho saputo esporre chiaramente i miei dubbi (cosa tipica di
chi affronta problemi per lui nuovi), ma per fortuna qualcun altro è
riuscito in qualche modo a decifrare il mio criptico messaggio e mi ha già
risposto, abbassandosi umilmente al mio ignobile livello.
Ciao,


 risposto, abbassandosi umilmente al mio ignobile livello.
Guarda che non avevo nessuna intenzione nè di offenderti nè tantomeno di
entrare in un contenzioso a base di "potevi rispondermi così" e "ti ho
risposto cosà".
Se chiedi informazioni o aiuto in un NG, mi spiace dirlo, non puoi
pretendere che tutti ti rispondano come vuoi tu. E a mio avviso non puoi
prendertela con chi ti ha dato risposte che non ti soddisfano.
Detto ciò, sarò lieto di aiutarti in altre occasioni, se ne sarò in grado e
se ne avrò il tempo, e se ti accontenti di informazioni che possono essere
riduttive, parziali, solo accennate, o anche sbagliate, senza per questo
sentirmi aggredito perchè non ti ho dato informazioni a te utili. Mi
piacerebbe di più se volessi spiegarmi meglio il tuo problema, che potrei
non aver capito.
--
Alessandro Monti

 lo standard e non c'hai problemi"":).
L'unica piccola stranezza del tuo compilatore (che comunque non
lo pone in violazione dello standard) e` che ti da` solo un warning,
invece che un errore, quando assegni l'indirizzo di a all'int* b.
Non per questo puoi apportare alcuna modifica a *b sinche` esso
punta a una locazione di memoria definita come const; cio` provoca
comportamento undefined.  Certo sarebbe bello che il compilatore
avesse posto quella locazione in una pagina read-only (se il tuo
sistema operativo supporta questa nozione), cosi` da darti una
"segmentation violation" o simili (probabilmente sarebbe stato
piu` chiaro), ma lo standard non lo vincola a farlo (anche perche`
non sarebbe fattibile su quei SO che il concetto di pagine read
only non lo hanno:-).
Alex

prendertela con chi ti ha dato risposte che non ti soddisfano.
Mi avrebbe fatto solo piacere una risposta insoddisfacente. La tua, mi sarò
certamente sbagliato, mi era sembrata ironica e "sapientona".
Ti ringrazio per la disponibilità, sarò felice di ricevere tuoi
suggerimenti. Scusa se ho dato l'idea di voler "aprire un contenzioso", non
era mia intenzione e certe "liti" non servono a chi segue il NG (io non sono
tra questi, sto studiando il c++ e di tanto in tanto vengo a porre i miei
dubbi).
Peace&Love,


 di memoria. Ma sicuramente mi sfugge qualcosa.
Il secondo dato "dummy" viene probabilmente creato solo se uno va a
cercare di modificare la costante.
EMG

Tra l'altro a non e` un puntatore, quindi "*a" non e` definita.
Per forza, ho scritto male! Volevo dire "(*b)++". Questa istruzione viene
eseguita e *b assume valore 9 (mentre a (che io ritenevo essere la stessa
cosa di *b) rimane 8). Se detto questo non hai altro da aggiungere, la
faccenda mi è comunque più chiara dopo aver letto le tue osservazioni su
certe strategie di ottimizzazione sulle quali non avevo riflettuto.
Ciao e grazie ancora,


...


 Non puo` essercene piu` di uno, se no si avrebbe ambiguita`.
Dovrebbe essere possibile avere fino a 4 copy constructor,
sfruttando tutte le combinazioni di const e volatile:
  class X {
    ...
    X( X& );
    X( const X& );
    X( volatile X& );
    X( const volatile X& );
  };
Per risolvere le ambiguita' si usano le normali regole
di overload resolution.
Esempio:
 
  X x;
  const X cx;
  X x1( x );   // usa X( X& )
  X x2( cx );  // usa X( const X& )
	Mauro

Sarei curioso di conoscere un caso pratico in cui sarebbe utile avere
QUATTRO costruttori di copia distinti per una stessa classe.
Ciao,
Nicola

Il costruttore di default costruisce un oggetto senza informazioni
dall'esterno, mentre il costruttore di copia, come dice il nome stesso,
inizializza un oggetto in base ad un altro della stessa classe.
L'argomento del costruttore di copia non e' l'oggetto da costruire, ma
quello da cui prendere le informazioni. Ad es:
class A {
  public:
    A() : i(2) {}
    A(const A & a) : i(a.i) {}
    void set(int n) { i = n; }
    int get() const { return i; }
  private:
    int i;
};
int main(int argc, char **argv) {
  A a;
  cout << "a: " << a.get() << endl;
  a.set(3);
  A b(a);
  cout << "b: " << b.get() << endl;
}
A meno di miei errori (non ho provato a compilare) dovrebbe stampare:
a: 2
b: 3
E' piu' chiaro?
Ciao,
Nicola

 sfruttando tutte le combinazioni di const e volatile:
Secondo il paragrafo 12.1 dello Standard, sub 10, volatile
non e` ammesso: il parametro del copy constructor puo`
essere solo X& o const X&.  12.8, sub 2, invece e` in
esplicito accordo con la tesi da te espressa.  Penso che
questa contraddizione nello standard vada risolta nel senso
di fare prevalere 12.8 sub 2 (cioe` che in effetti anche
volatile e` ammesso, e sono accettati gli overload), visto
che 12.1 sub 10 e` una menzione abbastanza tangenziale,
con un esplicito rimando a 12.8, e puo` facilmente aver
commesso un piccolo errore di omissione non nominando
volatile, mentre 12.8 e` dettagliata ed esplicita.
Alex

 QUATTRO costruttori di copia distinti per una stessa classe.
Non vedo praticamente nessun caso pratico in cui un
overload basato su volatile abbia reale utilita`.  Se ne
trovi uno, si potra` sicuramente anche mettere sotto
forma di un copy ctor.
Alex

 >cercare di modificare la costante.
Perche` dunque dovrebbe essere "demente" che il compilatore sia
tenuto a porre veramente in memoria solo cio` di cui tu prendi e
usi l'indirizzo?
Sia per le costanti, sia per le variabili, se non prendi mai un dato
indirizzo, ovvero se lo prendi ma non ne fai nessun uso, mi pare
_ovvio_ che il compilatore non e` tenuto a sprecare una parola
di memoria per conservarle se, visto il loro uso, possono vivere in
registri o (nel caso di costanti) direttamente come campo di una
istruzione di macchina.
Visto che fra "ovvio" (come pare a me) e "demente" (come pare
a te) c'e` un oceano, amerei capire meglio perche` tu debba mai
pensare che sia "demente" fare un'ottimizzazione cosi` "ovvia".
Alex

 QUATTRO costruttori di copia distinti per una stessa classe.
Non penso che esista.
Ci possono essere casi (abbastanza rari) in cui puo'
essere utile averne due (const e non-const), ma quattro
mi sembrano troppi.
	Mauro

pensare che sia "demente" fare un'ottimizzazione cosi` "ovvia".
Perchè non avevo considerato il processo di ottimizzazione di cui parlavi
tu. Quello che può sembrare demente è in realtà il risultato finale dei vari
processi del compilatore, che ti portano ad avere da un lato un dato
costante e da una parte un puntatore a tale dato che in realtà punta da
un'altra parte, e a questo proposito riterrei una giusta scelta sostituire
il warning di cui parlavo qualche msg fa con un errore, ma queste sono
scelte implementative che vanno al di là di quello che interessa a me. Ora
ho capito e non mi sembra più demente, grazie per il chiarimento.


 costante e da una parte un puntatore a tale dato che in realtà punta da
Se posso dire la mia, mi pare strano (non "demente", che mi pare una parola
pericolosa da usare) modificare un valore const. Se prima lo dichiariamo
costante e poi lo modifichiamo, stiamo già facendo noi qualche cosa di molto
strano, e il compilatore potrebbe non capire cosa gli vorremmo far fare.
Forse bisognerebbe aggiungere qualche keyword "sometimes", "thistime" e
"nonconst". Dunque:
sometimes const int a=8;
...
cout << a;    // Possibile ottimizzazione da parte del compilaore
cout thistime nonconst a;    // Nessuna ottimizzazione
Ma temo che l'ISO non lo accetterebbe :-)
--
Alessandro Monti

 ho capito e non mi sembra più demente, grazie per il chiarimento.
Prego!  E grazie a te per la cortesia dei tuoi post.
Alex

 Ma temo che l'ISO non lo accetterebbe :-)
Per l'unico uso "non totalmente peculiare", cioe` quello di avere
alcuni _membri_ non costanti entro una istanza di struct (o class)
che nel suo complesso _e`_ costante, esiste gia` la keyword
mutable.
Se un oggetto e` const nel senso che _il mio codice_ non lo
modifica, ma _altro codice_ potrebbe anche farci cose strane
(per cui il compilatore non deve fare affidamento sul suo
valore, bensi` "rileggerlo" ogni volta), c'e` apposta la
combinazione "const volatile":
    const volatile int a = 8;
    stranaChiamataEsterna(&a);
    cout << a;    // se il compilatore ottimizza qui, sbaglia
Vero e` che queste caratteristiche sono legate all'oggetto
a per tutta la sua "vita" (o scope), e non possono essere
modificate al volo dicendo "da questo punto questa const
e` volatile ... ok, da adesso non lo e` piu`", come la tua
scherzosa proposta permetterebbe.  La cosa meravigliosa,
ovvero orrenda (propendo per orrenda!-) e` che si puo`
avere una specie di effetto simile giocando con svariati
reference:
    const int a = 8;        // di per se` non volatile
    const int& ra = a;     // non volatile
    const volatile int& vra = a;    // ma questa si`
Ora, il compilatore potrebbe ottimizzare gli usi di a o
di ra, ma non quelli di vra (almeno, se l'indirizzo di a
o ra o vra viene preso da qualche parte; se no, non
riesco a determinare scervellandomi sullo standard se
l'ottimizzazione sia ammessa o meno!!!-).
Il problema numero 1 (e 2, e 3, e 4...) del C++ e` la
sua tremenda complessita`, e l'incredibile sottigliezza
di certi "distinguo" cui essa costringe.  Non a caso, sia
i compilatori (soprattutto gli ottimizzatori), sia gli utenti
del linguaggio (anche i programmatori piu` esperti), non
di rado si sbagliano su qualche punto apparentemente
"di lana caprina", con conseguenze imprevedibili.  Ahime`,
questa complessita` pare inevitabile per permettere al
C++ di soddisfare _tutti_ i suoi ambiziosi requisiti di
progetto (e, in effetti, li soddisfa, secondo me) -- fra
questi requisiti non c'e` mai stato "facile da usare", men
che meno "facile da usare bene", e neppure "facile da
compilare", soprattutto non "facile da ottimizzare"...!-)
Quindi, C++ fa bene le cose (difficilissime) che e` stato
progettato per fare -- ma, naturalmente, se uno appena
appena non ha bisogno di _tutte_ quelle cose (qualche
compatibilita` col C, prestazioni stratosferiche, gestione
di progetti giganteschi...), sarebbe furbo usare linguaggi
differenti.  Io, ad esempio, pur non essendo familiare a
fondo con Java e Visual Basic (non tanto quanto con C++,
e alcuni dei loro difetti mi vanno proprio di traverso!-), ho
notato un aumento della mia produttivita` di circa un
15/20%, quando uso quei linguaggi invece del C++; in
Perl 5, che conosco piuttosto benino, di circa un 100%;
in Python, che mi piace molto ma sto ancora soltanto
imparando, di circa un 200%.  Non sono noccioline!-)
Alex

strano, e il compilatore potrebbe non capire cosa gli vorremmo far fare.
"demente" non era ovviamente un termine tecnico con presunzione di
efficienza o precisione semantica=). Sono d'accordo con te sul fatto che i
valori const se sono const lo saranno per un motivo e quindi il compilatore
dovrebbe curarsi di mantenerli sempre immodificabili e, come ho già
specificato, di produrre un bell'errore in compilazione.


Prego!  E grazie a te per la cortesia dei tuoi post.
Oh meno male, pensavo che certe mie pignolerie fossero state prese in
antipatia. Ciao!


 imparando, di circa un 200%.  Non sono noccioline!-)
Idem, magari anche con percentuali molto (ma molto) più elevate. Ora però
sto cercando di tornare un po' al C++, dato che per certe cose la velocità
serve.
Come al solito, il linguaggio giusto per il progetto giusto. :-)
(o magari per parte di esso)
--
Alessandro Monti

 (o magari per parte di esso)
Quest'ultima e` un'altra osservazione che mi sembra molto
importante e molto giusta.  In generale, la scelta ottimale per
un dato progetto comprendera` molteplici linguaggi; spesso,
uno "di sistema" (tipo C++) per relativemente piccole parti
che pero` sono cruciali per prestazioni/security/ecc ecc, uno
"di scripting" (tipo Python, o Visual Basic) per il controllo
complessivo, le parti piu` fluide della "business logic" (che
spesso sono la maggioranza:-), la GUI, i test, ecc ecc.
Alex

spesso sono la maggioranza:-), la GUI, i test, ecc ecc.
Perfettamente d'accordo. Per tornare all'esempio dello sviluppo di un
videogame: il gioco vero e proprio (motori e IA) sono quasi sempre in C/C++.
I vari tool esterni necessari (quali level editor) si possono
tranquillamente affidare nelle mani del caro VBasic.


 tranquillamente affidare nelle mani del caro VBasic.
Chissà perché, un bell'editor di livelli oggigiorno lo vedo molto molto male
in VBasic... Ma siamo OT :)
-----
Giovanni Bajo
Lead Programmer
Prograph Research S.r.l.
http://www.prograph.it

in VBasic... Ma siamo OT :)
Ovviamente dipende da quello che ti deve editare e effettivamente se
guardiamo a produzioni odierne il VBasic appare limitato. Questo avvalora
ancora di più la tesi secondo cui con il C non si possono fare "POCHE"
cose... ma hai ragione, siamo OT=).
