Newsgroup: it.comp.lang.c++
Titolo: Problema static

Ciao ragazzi,
qualcuno mi potrebbe spiegare per cortesia cosa c'è che non va nel
codice di sotto?
Il linker si lamenta dicendo
Error: Unresolved external 'Pizza::pomodoro' referenced from module
cibo.cpp
class Pizza
{
	static int pomodoro;
	void condisci(int qty);
};
void Pizza::condisci(int qty)
{
	pomodoro = qty;
}
Se serve vi spiego più in dettaglio cosa vorrei fare, magari c'è una
soluzione alternativa.
Grazie, giulio
Per favore togliete "!RemoveMe!" dall'indirizzo per rispondere via email
Please remove "!RemoveMe!" from the address to email me

Scusate, ho risolto, basta mettere
int Pizza::pomodoro;
anche se non capisco bene perché...
Grazie lo stesso, giulio


 anche se non capisco bene perché...
Perche` non basta dichiarare le cose, bisogna anche
definirle.
Alex

 };
Aggiungi da qualche parte (presumibilmente in pizza.cpp) una riga:
    int Pizza::pomodoro;
e vivi felice e contento.
Alex

Perche' quando dichiari una classe descrivi come e' fatta, ma non
riservi spazio, neanche per i membri statici. Per allocare lo spazio
devi definire la variabile membro da un altra parte.
Puoi vederla anche da un altro punto di vista: la dichiarazione della
classe ti serve in tanti posti diversi (ovunque la usi), il membro
statico deve essere definito una volta sola.
Ciao,
Nicola

definirle.
Sě, lo so, ma non capisco perché solo per i data member di tipo
static, non puň capirlo da solo il compilatore?
Ciao, giulio


 static, non puň capirlo da solo il compilatore?
I data member che static non sono vengono istanziati
ex novo per ogni oggetto della tua classe.  Ne consegue
che non occorre "definirli", cioe` "dire esattamente dove
stanno", perche` "stanno" in posti gia` ben definiti (in
ciascun oggetto di quella classe).  Non vi sono altresi`
problemi di inizializzazione, poiche` i membri dati non
static vanno inizializzati da ogni costruttore di ogni
oggetto (se non esplicitamente, col ctor di default).
I data member static devono venire istanziati in UN
esemplare, ne` piu`, ne` meno.  Quell'esemplare
unico deve risiedere in UN singolo posto, e in questo
singolo posto deve avere la sua specifica inizializzazione
(eventualmente col ctor di default).
Si`, sarebbe sicuramente stato possibile progettare
un linguaggio, diverso dal C++, in cui l'assenza, ovvero
la duplicazione, della definizione di un membro dati
static, invece di essere un errore, come lo e` in C++,
avesse altre, arbitrarie valenze semantiche.  In C++,
invece, vale per molte cose la "one-definition rule",
che impone che le cose vengano definite una ed una
sola volta -- non zero volte, non sette volte, ma una.
Sembra, tutto sommato, ragionevole che il C++ scelga
di imporre questa semantica, poiche` garantisce il
programmatore contro la distrazione consistente
nel non fornire alcuna definizione (e quindi, forse,
essersi scordato l'inizializzazione), ovvero nel darne
piu` di una (come ammette, ad esempio, il linguaggio
Fortran per i DATA dei COMMON; se ne dai piu` di
una, il compilatore ne puo` scegliere una qualsiasi,
a caso e/o a buzzo suo -- se non ne dai nessuna, se
ne puo` inventare una ad assoluto piacimento).  In
Fortran, infatti ed ad esempio, questa e` una notoria
causa di errori (sezioni DATA di COMMON omesse o
in conflitto).
Per analogia con altri elementi per cui vale la
one-definition rule, come le funzioni:
    oggi, se intendevi scrivere la definizione
        int Pizza::Pomodoro = 23;
    e te la scordi, il linker ti sgrida;
    esattamente come se, avendo dichiarato
    una funzione static che intendevi definire:
        int Pizza::Pummarola() { return 45; }
    il linker identicamente ti sgrida;
    preferiresti che, essendoti tu dimenticato
    di definire Pizza::Pummarola, il linker
    silenziosamente ne creasse una versione
    che ritorna sempre zero?
    preferiresti che, essendoti tu dimenticato
    di definire Pizza::Pomodoro, il linker
    silenziosamente ne creasse una versione
    inizializzata a zero?
Se la tua risposta ad entrambi i "preferiresti"
e` "si`", ti consiglio di pensare seriamente a
passare a un altro linguaggio; il C++ non ha
questa filosofia di base, di fare silenziosamente
e di nascosto le cose per conto del programmatore
e a sua insaputa, che era tipica ad esempio del
PL/I.  (Purtroppo alcune, rare cose, come i
costruttori di default e gli operatori di assegnazione,
sono invece gestite con questa filosofia PL/I-esca,
qui per remote ragioni storiche relative alla
compatibilita` delle classi con le struct del C).
Se la tua risposta ai due "preferiresti" e` diversa,
se cioe` cerchi un linguaggio ricco, eclettico,
incoerente, una bella collezione di soluzioni ad
hoc, ti suggerisco caldamente il Perl (linguaggio
che amo -- per cosette non oltre, diciamo, le
1000 righe di codice o giu` di li` -- ma che
sarebbe un inferno usare per i programmi piu`
sostanziali, centinaia di migliaia di righe di
codice, per i quali si usa solitamente il C++).
Alex

static, non puň capirlo da solo il compilatore?
I data member statici essendo comuni a tutte le istanze della
classe vengono trattati diversamente, in particolare il linguaggio
prevede che essi vengano definiti e inizializzati a parte fuori dalla
classe e non in un costruttore (che verrebbe eseguito ad ogni
istanziazione della classe).
D'altronde la dichiarazione di classe e` appunto solo una dichiarazione
e non riserva alcuna memoria...
Questo in linea di principio, ci sono poi i casi particolari
dei data member static const che limitatamente ai tipi
integrali possono essere dichiarati, definititi ed inizializzati
in un colpo solo all'interno della classe.
Paolo
-----------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------

 
Dichiarati ed inizializzati si, ma definiti devono esserlo ancora fuori
dalla dichiarazione della classe, anche se quando li usi solo
all'interno della dichiarazione della classe i compilatori possono non
lamentarsi della assenza di definizione. Un esempio perche' quanto
precede e' un po' criptico:
// A.h
class A {
  int f();
  static const int N1 = 2;
  static const int N2 = 3;
  char s[N1];
};
// A.cpp
#include "A.h"
const int A::N2;
int A::f() { return N2; }
Nell'esempio N1 potrebbe anche non essere definita in A.cpp perche' e'
usata solo nella definizione della classe, N2 deve essere definita
all'esterno perche' usata nell'implementazione di f.
Ciao,
Nicola

 dalla dichiarazione della classe, anche se quando li usi solo
La definizione fuori dalla dichiarazione della classe e'
necessaria se e solo se il membro deve risiedere in memoria
(per esepmio se si usa il suo indirizzo).
Esempio:
class Pippo {
pulbic:
  static const int A = 1;
  static const int B = 2;
};
const int Pippo::A;  // necessaria per l'inizial. di p
int * p = &Pippo::A;
// si usa l'indirizzo di Pippo::A, quindi da qualche
// parte ci deve essere la definizione.
int a = Pippo::B;
int func(int x) { return Pippo::B; }
func( Pippo::B );
// qui si usa solo il valore di Pippo::B, e non e' necessario
// che Pippo::B si trovi in memoria, quindi si puo' omettere
// la definizione "extra".
	Mauro

In effetti il draft dello standard pare dire cosi`...
Ho pero` riscontrato compilatori che non richiedono
la definizione (senza inizializzazione) fuori dalla classe.
Comunque e` lo standard che fa testo!


 compatibilita` delle classi con le struct del C).
Un altro aspetto abbastanza lontano da quella solida filosofia di base
mi sembrano le regole per l'istanziazione dei template. O sbaglio?
EMG

 mi sembrano le regole per l'istanziazione dei template. O sbaglio?
Mah, non mi sembra che, istanziando un template, il C++ 
faccia checche` "all'insaputa" del programmatore.
La deduzione automatica dei tipi (per template di funzioni)
puo` a volte "stupire", lo ammetto.  Ad esempio:
template <class T>
T accumula(T t)
{
    static T accumulatore = 0;
    return accumulatore+=t;
}
    float f=1.5;
    std::cout<<accumula(f)<<'\n';
    std::cout<<accumula(f)<<'\n';
    std::cout<<accumula(1.5)<<'\n';
    std::cout<<accumula(1.5)<<'\n';
    std::cout<<accumula(f)<<'\n';
    std::cout<<accumula(1.5)<<'\n';
Molti si aspetterebbero, senza dubbio, di vedere una
sequenza monotona crescente 1.5, 3.0, 4.5, 6.0, ...,
mentre in realta` vedranno 1.5, 3.0, 1.5, 3.0, 4.5 ...
Questo perche` l'instanziazione del template accumula<>
e` separata (sono due funzioni diverse) per T=float e
T=double; e la costante scritta "1.5" e` double, _non_
float.
Senza dubbio, se il C++ imponesse di scrivere ogni
volta, esplicitamente, accumula<float>, ovvero
accumula<double>, cioe` se i parametri dei template
dovessero sempre essere espliciti, invece di venire
dedotti, questo particolare malinteso non si avrebbe.
Non per questo mi pare si possa dire che la deduzione
dei tipi viola la (solida) filosofia fondamentale di "non
fare le cose di nascosto".  Che 1.5 e` un double e non
un float, o, similmente, che 1 e` un int, e non un
float ne` un unsigned, ecc ecc, e` un assunto di base,
fondamentale, del linguaggio.  Se il programmatore ha
un malinteso su questo, non e` solo la deduzione dei
tipi nell'istanziazione dei template che lo "mordera`" --
egli, infatti, avra` simili problemi anche su aspetti come
gli overload, ad esempio.
Devo certo ammettere che, come si puo` pensare ad
un linguaggio che fa molte cose "implicitamente", molte
piu` del C++ (come, ad esempio, Perl, o PL/I), cosi` se
ne puo` pensare uno che ne fa molte di meno (C--?-),
e in esso, oltre a non avere la generazione implicita di
costruttori e assegnamento di default che avevo gia`
nominata, si potrebbero togliere anche aspetti come
gli overload, i parametri di default, la deduzione dei tipi
dei template (di funzioni), l'istanziazione dei template,
e magari, perche` no, anche l'esecuzione automatica,
e piu` o meno implicita, dei costruttori e dei distruttori --
tutti aspetti che possono confondere un programmatore
privo di una solida preparazione sul linguaggio.
Non mi pare, pero`, che un linguaggio cosi` impoverito
rispetterebbe meglio la filosofia di "non fare le cose di
nascosto".  A parte le confusioni dei principianti, gli
errori di omissione che il linguaggio C++ come oggi si
presenta e` impossibilitato a diagnosticare (se non al
massimo come warning -- si sa che i warning possono
liberamente essere dati per TUTTO, anche "warning,
hai una funzione di piu` di sei righe, perche` non la
spezzi in due che magari e` meglio?":-) si incentrano
a mio vedere sulla generazione "implicita" dei
costruttori di default e di copia e dell'assegnamento
di copia.  I template non mi pare presentino simili
vulnerabilita` ad _errori di omissione_, o, almeno, non
me ne viene in mente un esempio; puoi offrirne...?
Alex