Newsgroup: it.comp.lang.c++
Titolo: dubbio su new

Saluti.
Da un po di tempo ho un dubbio su come si comporta new.
Considerate il seguente esempio
#include <iostream>
main()
{
  int* p = new int[10];
  for(size_t i = 0; i < 10; ++i) std::cout << p[i] << '\n';
}
Quello che non ho capito è se new esegua o meno il costruttore.
Il dubbio sorge dal fatto che questo programma non visualizza
tutti zeri (sia usando il BCC che il DJGPP).
Tuttavia se ho una classe
class Test
{
  private:
    Test();
};
è illegale fare Test* pT = new Test[10];
perchè dice che il costruttore di Test è privato.
Attendo delucidazioni a riguardo,
grazie    Manlio Perillo

 perchè dice che il costruttore di Test è privato.
Appunto, come detto prima, new esegue i costruttori.
--
***
Ertai, Wizard Adept
L'intelligenza sta / nell'esser prudenti
quando cala una nebbia / sul rischio di cadere
Troppo facile avere / La lucidità di quelli che
Non sanno camminare
***

 wrote :
| Saluti.
| Da un po di tempo ho un dubbio su come si comporta new.
| Considerate il seguente esempio
|
| Quello che non ho capito è se new esegua o meno il costruttore.
| Il dubbio sorge dal fatto che questo programma non visualizza
| tutti zeri (sia usando il BCC che il DJGPP).
Chi ha detto che gli int hanno un costruttore? e che questo istanzi 0?
| Tuttavia se ho una classe
|
| class Test
| {
|   private:
|     Test();
| };
|
| è illegale fare Test* pT = new Test[10];
| perchè dice che il costruttore di Test è privato.
ci credo: lo hai dichiarato privato! Deve avere accessibilita' pubblica, se
devi usarlo direttamente (come fa new), altrimenti solo funzioni e classi
dichiarate friend *all'interno* della dichiarazione di Test, potranno
invocarlo.
prova
#include <iostream>
class Test {
    int dato;
    public:
        Test() : dato(0) {}
// imposta dato a zero (col costruttore di copia, pero!)
    void stampa() { std::cout << "dato = " << i << endl; }
}
int main(){
    Test *arr = new Test[10];
    for (i= 0; i < 10 ++i)
        arr[i].stampa();
    return 0;
}

 tutti zeri (sia usando il BCC che il DJGPP).
Il costruttore di default di int, se invocato _implicitamente_
su variabili con lifetime di blocco ("automatiche"), o con
lifetime dinamico, non e` tenuto a inizializzare la memoria; se
l'invocazione e` esplicita, o se il lifetime della variabile e`
"statico", invece si`.
Si puo` anche inquadrare la problematica in termini diversi,
ma la sostanza e` questa...
Alex

 Chi ha detto che gli int hanno un costruttore? e che questo istanzi 0?
Prova, ad esempio:
    #include <iostream>
    void prova(int i) {
        std::cout << "Vale: " << i << "\n";
    }
    int main()
    {
        prova( int() );
        return 0;
    }
A norma di Standard, questo deve stampare "Vale: 0".
Il punto e`, piuttosto, che questo costruttore non e` tenuto a
inizializzare la memoria se agisce implicitamente su variabili
di lifetime automatico o dinamico; ma lo zero c'e`, in caso di
invocazione esplicita o lifetime statico.
O, se preferisci, puoi dire che il costruttore di default non
e` invocato automaticamente per i tipi built-in eccetto che nel
caso di lifetime statico.  L'effetto netto e` lo stesso.
Alex

 wrote in message
| Il punto e`, piuttosto, che questo costruttore non e` tenuto a
| inizializzare la memoria se agisce implicitamente su variabili
| di lifetime automatico o dinamico; ma lo zero c'e`, in caso di
| invocazione esplicita o lifetime statico.
Questa non la sapevo. Ho infatti l'abitudine di istanziare esplicitamente
tutto cio' che mi serve, prima di usarlo.
IMHO, in questo modo il codice risulta piu' comprensibile a colpo d'occhio.

 wrote in message
news:384ff...@news.megasys.it...

Si`, ma una saggia eccezione a questa prassi si ha quando un
valore dev'essere ottenuto dalla chiamata a una funzione che
lo "ritorna" attraverso pointer o reference e non come valore
di ritorno.
Supponiamo, ad esempio, di avere in una qualche API esterna
una funzione
    void CuccaTutto(int* pUno, int* pDue, int* pTre);
con la specifica che i tre puntatori che accetta sono "di
output" -- essa pone i suoi risultati a quegli indirizzi.
Il modo migliore di chiamare questa funzione e` di solito:
    int uno, due, tre;
    CuccaTutto(&uno, &due, &tre);
Inizializzare (inutilmente) le tre variabili non e` saggio,
e cio`, non tanto per i 'bananosecondi' cosi` eventualmente
sprecati, quanto perche` la prassi e` "implicitamente"
ingannevole.
Analogia: supponi che io dica: "Non uccido mai gli ospiti
di martedi`".  Ora, questo e` perfettamente vero.  Pero`,
a sentirmelo dire, qualcuno che io avessi invitato per un
mercoledi` dovrebbe giustamente preoccuparsi... *perche`*?
Perche` giustamente si chiede: Perche` ha detto "di
martedi`"?!  Se lo ha detto e` segno che bisognava dirlo...
Non si moltiplicano le entita` senza necessita`; non si
precisa "di martedi`" per qualcosa che vale tutti i giorni
della settimana; non si setta esplicitamente un valore che
si sa (dalle specifiche di CuccaTutto) verra` ignorato e
sovrascritto immediatamente.  Quindi chi fa un'asserzione
come questa sta *implicando* che negli altri giorni della
settimana essa potrebbe non valere, e chi scrive in modo
esplicito int uno=1, due=2, tre=3; sta implicando che
questi valori iniziali possono venire (almeno in qualche
caso) usati come tali...
Psicologia umana, se vuoi, ma che altro e` il 90% della
prassi di programmazione?-)
Alex

 wrote in message 8kdio7$957
| Inizializzare (inutilmente) le tre variabili non e` saggio,
| e cio`, non tanto per i 'bananosecondi' cosi` eventualmente
| sprecati, quanto perche` la prassi e` "implicitamente"
| ingannevole.
Dev'essere un vizio che ho preso passando struct alle API dell'SDk di win:
di solito vanno inizializzate, molte addirittura con la propria dimensione,
visto che molte ritornano dati in quelle strutture :)
esempi famosi
  CreateProcess
  Le DirectX
Oppure lo faccio in modo da avere un risultato imossibile, da poter
verificare per test:
ad esempio:
void leggi_termostati(int* t1, int* t2, int* t3);
sapendo che il range di valori e' -1.000.000 / 1.000.000 per quei sensori,
int temp1, temp2, temp3;
temp1 = -2000000;
temp2 = -2000000;
temp3 = -2000000;
leggi_termostati(&temp1, &temp2, &temp3);
if ((temp1 < -1000000) || (temp1 > 1000000))
    throw t_exception(temp1);
o roba del genere.
se la funzione per qualche motivo (ad esempio i termostati non vanno)
ritorna senza leggere i valori, te ne accorgi.

 visto che molte ritornano dati in quelle strutture :)
Certo, ma in questi casi in C++ si fa un'eredita` di comodo, tipo:
    struct StartupInfo: STARTUPINFO {
        StartupInfo() {
            memset(this, 0, sizeof(this));
            cb = sizeof(this);
        }
    };
e cosi` via, una volta per tutte in un header di comodo.  Poi si dichiara
    StartupInfo si;
e si passa &si dove e` richiesto il puntatore a STARTUPINFO.  Non
ripeterai mica il codice di inizializzazione tutte le volte che ti serve?
*Fallo UNA volta E UNA SOLA* e` una regola d'oro della programmazione.
Alex

 wrote in message
| > di solito vanno inizializzate, molte addirittura con la propria
| dimensione,
| > visto che molte ritornano dati in quelle strutture :)
|
| Certo, ma in questi casi in C++ si fa un'eredita` di comodo, tipo:
|
|     struct StartupInfo: STARTUPINFO {
|         StartupInfo() {
|             memset(this, 0, sizeof(this));
|             cb = sizeof(this);
|         }
|     };
|
| e cosi` via, una volta per tutte in un header di comodo.  Poi si dichiara
|     StartupInfo si;
| e si passa &si dove e` richiesto il puntatore a STARTUPINFO.  Non
| ripeterai mica il codice di inizializzazione tutte le volte che ti serve?
| *Fallo UNA volta E UNA SOLA* e` una regola d'oro della programmazione.
Si, ma cosi' diventa implicito, e addio al promemoria di cui sopra (almeno
per me). Io uso delle funzioni template:
template <class T>
bool struct_set(T&
    // memset, etc etc
}
con un codice simile a quello di cui sopra, e casomai specializzo il
template su struct particolari: cosi' almeno la chiamata e' esplicita, e
non mi impazzisco a capire perche' mi ritorna zero.
"Lo scrivo una e una sola volta, ma la richiamo esplicitamente", IMHO, in
alcuni casi e' meglio.

 ritorna senza leggere i valori, te ne accorgi.
Mi sembra che dovrebbe semmai essere la funzione leggi_termostati() a
restituire un codice di errore o direttamente a generare l'eccezione.
Perché farlo ogni volta dall'esterno dato che è un problema strettamente
legato a quella funzione?
EMG

 alcuni casi e' meglio.
L'esplicitezza (esplicitudine?-) e` una buona cosa, ma, secondo me,
solo quando c'e` ragione di essere espliciti.  I progettisti dello standard
C++ apparentemente la pensano allo stesso modo; infatti, per dare due
esempi, il piu` banale e il piu` astruso:
-- visto che 99% delle volte il main() si chiude con
        return 0;
    }
    in C++ standard questo specifico "return 0" puo` essere omesso, e
    diventa allora IMplicito.  Meglio!  Cosi` ci abitueremo che "return"
    esplicito nel main *SIGNIFICA* qualcosa.  (Quando i compilatori si
    degneranno di supportare questa radicale innovazione...:-).
-- visto che 99% delle volte quando istanzio uno qualsiasi dei vari
    template standard con char, uso poi come traits i char_traits<char>,
    il comunissimo caso
        std::undatotemplate<char, std::char_traits<char> >
    posso esprimerlo invece come
        std::undatotemplate<char>
    e il secondo parametro ha i char_traits<> come DEFAULT, cosi`
    che posso lasciarlo implicito; cosi`, quando vedo un parametro
    traits esplicito, so che *SIGNIFICA* qualcosa.
Secondo me, il fatto che una strutturona da passarsi alle API di
Windows debba essere tutta azzerata (meno quei campi, di
solito una minoranza, che voglio esplicitamente settare) con
l'eccezione di un campo (che ha vari possibili nomi) che viene
settato al sizeof() della struttura, e` uno dei casi in cui il tutto e`
meglio lasciarlo implicito, perche` non *SIGNIFICA*, nel senso
intransitivo (e insolito, ma non incorretto:-) del verbo: non E`
SEGNO, non FA DA SEGNO, non RIMANDA A QUALCOSA.
(Come il fatto che una VARIANT appena dichiarata vada poi
soggetta a VariantInit, e prima di sparire a VariantClear: sono
esattamente le cose che in C++ si fanno con default ctor e
dtor, VARIANT e` priva di questi metodi solo perche` e` una
legacy dal C, la migrazione ottimale a C++ questi metodi li
aggiunge, e infatti cosi` sono fatte tutte le incapsulatrici di
VARIANT che io conosca; ecc, ecc).
Preferisco l'espliciteria (:-) per qualsiasi elaborazione di una
qualsiasi SOSTANZA; la pattern "2-phase costruction, 2-phase
destruction" (con l'importante eccezione delle classi che
incapsulano allocazione/deallocazione di risorse, fatte per
l'uso come automatic, nel qual caso il dtor ha un singolo
lavoro importante da compiere... deallocare la risorsa che
la classe incapsula).  Ma "azzera tutto e setta il size", "di`
che questa union e` nel caso ``empty''", ecc ecc, non le
considero elaborazioni sostanziali, _significanti_ (e dagli),
e quindi preferisco lasciarle implicite.
Comunque, rispetto anche il tuo stile, pur cosi` diverso dal
mio (e dal mainstream C++, che assomiglia al mio ma tende
a spingere l'implicitume anche piu` a fondo, spesso ignorando
i vantaggi di "2-phase").  L'importante e` tenere una certa
coerenza stilistica.  In Python, in particolare, "explicit is better
than implicit" e` considerato uno dei mantra-guida di stile!-)
Alex

 wrote in message
| Sandro wrote:
| > leggi_termostati(&temp1, &temp2, &temp3);
| >
| > if ((temp1 < -1000000) || (temp1 > 1000000))
| >     throw t_exception(temp1);
| >
| > o roba del genere.
| > se la funzione per qualche motivo (ad esempio i termostati non vanno)
| > ritorna senza leggere i valori, te ne accorgi.
|
| Mi sembra che dovrebbe semmai essere la funzione leggi_termostati() a
| restituire un codice di errore o direttamente a generare l'eccezione.
| Perché farlo ogni volta dall'esterno dato che è un problema strettamente
| legato a quella funzione?
perche' e' scritta in c, magari, e funziona da 10 anni egregiamente e
allora perche' metterci mano? Oppure non l'ho scritta io, ma qualche
scrittore di driver per le schede che uso: che ne so cosa faccia alle
variabili, specifiche a parte?
Eiffel ha questo gran pregio: almeno sai cosa aspettarti da una f() a
compile-time, e non a run-time.

 wrote in message
| Comunque, rispetto anche il tuo stile, pur cosi` diverso dal
| mio (e dal mainstream C++, che assomiglia al mio ma tende
| a spingere l'implicitume anche piu` a fondo, spesso ignorando
| i vantaggi di "2-phase").
Ora pero' non pensare che dichiaro anche la mammina esplicitamente!
Benvengano le std::string, ad esempio, con tutto il lavoro di sottofondo
implicito.
La cosa avviene soprattuto se uso codice "untested", come le API di un
driver di periferica di terzi, o una libreria che non ho mai usato prima, o
che so essere "buggy" (leggi MFC): in questi casi un po' di esplicitaggine
non fa male...
una curiosita' su 2fc-2fd: io uso init() e clear().
A quanto vedo da altri colleghi, mi sa che potrebbero essere proposte come
standard! :)

 Chi ha detto che gli int hanno un costruttore? e che questo istanzi 0?
Allora perchè posso fare:
int i = int();  // i = 0
Saluti  Manlio Perillo


Il mio dubbio sorgeva dal dichiarato pari trattamento riservato
a tipi definiti dall'utente e tipi predefiniti.
Ossia:
int i = int();    // implica i = 0
Saluti  Manlio Perillo

 variabili, specifiche a parte?
Certo. Allora io la incapsulerei in una funzione C++ (nel rispetto
dell'appena citato - in un altro thread - principio "fallo una ed una
sola volta"). Forse era proprio questo che si proponeva?
EMG

 wrote in message
| Certo. Allora io la incapsulerei in una funzione C++ (nel rispetto
| dell'appena citato - in un altro thread - principio "fallo una ed una
| sola volta"). Forse era proprio questo che si proponeva?
Perche' sovraccaricare una funzione il cui scopo dichiarato e' leggere
e riferire e basta? il controllo lo fa chi chiama, che sa cosa e come
controllare, se generare o meno l'eccezione...
si chiama KISS, se non sbaglio...
certo il problema della ownership e del controllo e' grosso, e non si
liquida qui con due parole...
in generale... dipende.

 standard! :)
Io di solito Create e Destroy, ma penso sia la cattiva
influenza di un passato MFC:-).
Alex