Domanda su C++
Salve ragazzi...ho iniziato da poco a programmare in C++ e avrei una domanda da porvi...quando si definiscono i prototipi delle funzioni(prima del main),ovvero tipo:
void input_vettore(............);
..
void output_vettore(.............);
tra le parentesi tonde trovo "cose" del tipo= (vettore V,int& riemp,int&pos) oppure (int l,int val,int pos)
volevo sapere,innanzitutto,la differenza tra int&variabile e int variabile...a cosa serve quella & commerciale?
e poi a volte ho trovato funzioni dichiarate così:
void input_vettore()
...
void output_vettore()
se facessi sempre così,cioè senza mettere niente tra le parantesi il programma funzionerebbe lo stesso?
void input_vettore(............);
..
void output_vettore(.............);
tra le parentesi tonde trovo "cose" del tipo= (vettore V,int& riemp,int&pos) oppure (int l,int val,int pos)
volevo sapere,innanzitutto,la differenza tra int&variabile e int variabile...a cosa serve quella & commerciale?
e poi a volte ho trovato funzioni dichiarate così:
void input_vettore()
...
void output_vettore()
se facessi sempre così,cioè senza mettere niente tra le parantesi il programma funzionerebbe lo stesso?
Risposte
Le tue domande dovrebbero trovare risposta in un buon libro sul C++. Se non stai seguendo un buon libro, prova questo (i riferimenti sono spiegati nel capitolo 3).
Il simbolo &, in quel contesto, significa riferimento. In C++ puoi avere variabili vere e proprie, e riferimenti a variabili. Esempi (T può essere un tipo qualsiasi, int, double, classe...):
Praticamente un riferimento serve a dare un altro nome ad una variabile. Nota che non puoi riassegnare un riferimento.
I riferimenti si usano come tipi per gli argomenti delle funzioni per due motivi:
1) permettere di modificare il valore originale
2) evitare di copiare variabili se sono "grosse"
A f1() viene passata una copia di "a", a f2() viene passato un riferimento costante, quindi in entrambi i casi la variabile "a" del main() non viene modificata. Però se il tipo T occupa molta memoria, per farne una copia serve molto* tempo. Invece un riferimento contiene semplicemente l'indirizzo di memoria della variabile, che occupa pochissimo spazio (4 o 8 byte in genere), e si copia in un attimo. Quindi per chiamare f2() si spende meno tempo che per chiamare f1().
In linea di massima i tipi primitivi (int, double, char, bool, puntatori) si passano per valore, mentre classi e struct si passano per riferimento costante. Con l'eccezione:
In questo caso non si guadagna nulla passando un riferimento costante, tanto poi viene comunque fatta una copia. Meglio usare direttamente f1().
* L'ordine di grandezza è probabilmente dei microsecondi, però se f1() viene chiamata milioni di volte la differenza si potrebbe sentire.
In C++ se una funzione è dichiarata senza argomenti significa che accetta 0 argomenti (a differenza del C dove accetterebbe qualsiasi numero e tipo di argomenti).
Prototipi e funzioni devono combaciare.
Il simbolo &, in quel contesto, significa riferimento. In C++ puoi avere variabili vere e proprie, e riferimenti a variabili. Esempi (T può essere un tipo qualsiasi, int, double, classe...):
T a = 3; // "a" è una variabile di tipo T. // Il compilatore fa spazio in memoria per un T. T b; // "b" è una variabile di tipo T. // Il compilatore fa spazio in memoria per un T. // "a" e "b" sono due variabili distinte. // Assegna ad "b" il valore di "a" (cioè 3). // Copia il valore di "a" in "b". b = a; // Modifica il valore di "a". a = 5; // Ora "a" = 5, "b" = 3
T a = 3; // "a" è una variabile di tipo T. // Il compilatore fa spazio in memoria per un T. T & b = a; // "b" è una variabile di tipo riferimento a T, ed è // un riferimento alla variabile "a". // Il compilatore fa spazio in memoria per un riferimento // ad una variabile. // Modifica il valore di "a". a = 5; // Ora "a" = 5, ma anche "b" = 5 poiché è un riferimento ad "a". // Modifica il valore di "b". Ma "b" non ha un valore, è un riferimento ad "a". b = 8; // Ora "b" = 8, ma anche "a" = 8.
Praticamente un riferimento serve a dare un altro nome ad una variabile. Nota che non puoi riassegnare un riferimento.
T a = 2, b = 34; T & c = a; // "c" è un riferimento ad "a", e lo sarà per sempre. c = b; // "c" NON diventa un riferimento a "b". // Invece, copia il valore di "b" in "c" (e quindi in "a").
I riferimenti si usano come tipi per gli argomenti delle funzioni per due motivi:
1) permettere di modificare il valore originale
void f1(T b) { // "b" è una variabile della funzione f1(). b = 3; } void f2(T & c) { // "c" è un riferimento ad una variabile. c = 5; } void f3(const T & c) { // "c" è un riferimento costante ad una variabile. c = 12; // Errore, non puoi modificare un riferimento costante. } int main() { T a = 1; // Il valore di "a" viene copiato nel primo argomento "b" della // funzione "f1()". f1(a); // Qui "a" vale ancora 1. // Alla funzione "f2()" viene passato un riferimento ad "a". f2(a); // Qui "a" vale 5. // Alla funzione "f3()" viene passato un riferimento costante ad "a". f3(a); // Qui "a" vale 5. }
2) evitare di copiare variabili se sono "grosse"
struct T { ... }; // T contiene molti campi, una variabile di tipo T occupa // molto spazio in memoria. void f1(T b) { ... } void f2(const T & c) { ... } int main() { T a = ...; f1(a); f2(a); }
A f1() viene passata una copia di "a", a f2() viene passato un riferimento costante, quindi in entrambi i casi la variabile "a" del main() non viene modificata. Però se il tipo T occupa molta memoria, per farne una copia serve molto* tempo. Invece un riferimento contiene semplicemente l'indirizzo di memoria della variabile, che occupa pochissimo spazio (4 o 8 byte in genere), e si copia in un attimo. Quindi per chiamare f2() si spende meno tempo che per chiamare f1().
In linea di massima i tipi primitivi (int, double, char, bool, puntatori) si passano per valore, mentre classi e struct si passano per riferimento costante. Con l'eccezione:
void f1(T b) { // Modifica "b". ... } void f2(const T & c) { T d = c; // Fa una copia di "c" // Modifica "d". ... }
In questo caso non si guadagna nulla passando un riferimento costante, tanto poi viene comunque fatta una copia. Meglio usare direttamente f1().
* L'ordine di grandezza è probabilmente dei microsecondi, però se f1() viene chiamata milioni di volte la differenza si potrebbe sentire.
In C++ se una funzione è dichiarata senza argomenti significa che accetta 0 argomenti (a differenza del C dove accetterebbe qualsiasi numero e tipo di argomenti).
void f() { ... } int main() { f(); // Ok f(3); // Errore, f() non accetta 1 argomento. }
Prototipi e funzioni devono combaciare.
Aggiungo solo qualche piccolo commento. La regola secondo cui si passano strutture e classi per riferimento e tipi primitivi per valore ha ovviamente anche come eccezione i casi in cui la struttura o la classe siano molto piccoli o quando sia necessario modificare questi tipi primitivi nella classe. Per cui è ovvio che se si scrive ad esempio una funzione per scambiare due valori interi sarà necessario usare riferimenti o puntatori (vedi sotto).
In modo simile, se si scrive una funzione che somma due vettori bidimensionali potrebbe non valere la pena di passarli per riferimento:
Insomma, ci sono casi in cui la scelta è evidente e altri in cui si sceglie in base a preferenze personali o ad altri fattori.
Come dicevo sopra esiste un altro tipo di variabile legato ai riferimenti, i puntatori. I puntatori sono nati prima dei riferimenti e hanno diversi vantaggi e svantaggi rispetto ad essi. Sono infatti più flessibili, ma i diversi problemi a loro associati hanno portato alla scelta di definire i riferimenti per rimediarvi. I puntatori, come i riferimenti, fanno riferimento ad un altra variabile. In effetti sono variabili che contengono l'indirizzo della locazione di memoria associata ad una certa variabile. Al contrario dei riferimenti, questo indirizzo si può cambiare in modo da farlo "puntare" a qualche altra variabile. Siccome, attraverso un puntatore, è possibile modificare sia l'indirizzo contenuto nel puntatore che il valore della variabile a cui punta, è necessario fare uso di una sintassi un po' più complicata.
C'è molto altro da dire sui puntatori. Sono allo stesso tempo la funzionalità più potente e più complicata del linguaggio C e che è stata portata poi nel C++. Come ho già detto, i riferimenti sono stati inventati proprio perché i puntatori hanno una sintassi più complicata e hanno diversi problemi (possono per esempio contenere indirizzi non validi). Il loro uso in C++ è molto più limitato rispetto a quello che si ha in C, ma è comunque utile conoscerne l'esistenza nel caso capiti di incontrarli. Un qualsiasi manuale di C++ dovrebbe trattarli in maniera molto approfondita e ti consiglio di andarti a leggere i capitoli corrispondenti a questi argomenti.
void swap(int &a, int &b) { int tmp = a; a = b; b = tmp; }
In modo simile, se si scrive una funzione che somma due vettori bidimensionali potrebbe non valere la pena di passarli per riferimento:
struct Vector { Vector(double _x, double _y) : x(_x), y(_y) {} double x; double y }; Vector sum(Vector a, Vector b) { return Vector(a.x + b.x, a.y + b.y); }
Insomma, ci sono casi in cui la scelta è evidente e altri in cui si sceglie in base a preferenze personali o ad altri fattori.
Come dicevo sopra esiste un altro tipo di variabile legato ai riferimenti, i puntatori. I puntatori sono nati prima dei riferimenti e hanno diversi vantaggi e svantaggi rispetto ad essi. Sono infatti più flessibili, ma i diversi problemi a loro associati hanno portato alla scelta di definire i riferimenti per rimediarvi. I puntatori, come i riferimenti, fanno riferimento ad un altra variabile. In effetti sono variabili che contengono l'indirizzo della locazione di memoria associata ad una certa variabile. Al contrario dei riferimenti, questo indirizzo si può cambiare in modo da farlo "puntare" a qualche altra variabile. Siccome, attraverso un puntatore, è possibile modificare sia l'indirizzo contenuto nel puntatore che il valore della variabile a cui punta, è necessario fare uso di una sintassi un po' più complicata.
T a = 0; // questa è una variabile di tipo T T *p; // questo è un puntatore che può contenere l'indirizzo di variabili di tipo T p = &a; // &a è l'indirizzo associato alla variabile a. // p a questo punto fa riferimento ad a *p = 5; // *p restituisce la variabile corrispondente all'indirizzo contenuto in p // a questo punto a conterrà 5
C'è molto altro da dire sui puntatori. Sono allo stesso tempo la funzionalità più potente e più complicata del linguaggio C e che è stata portata poi nel C++. Come ho già detto, i riferimenti sono stati inventati proprio perché i puntatori hanno una sintassi più complicata e hanno diversi problemi (possono per esempio contenere indirizzi non validi). Il loro uso in C++ è molto più limitato rispetto a quello che si ha in C, ma è comunque utile conoscerne l'esistenza nel caso capiti di incontrarli. Un qualsiasi manuale di C++ dovrebbe trattarli in maniera molto approfondita e ti consiglio di andarti a leggere i capitoli corrispondenti a questi argomenti.
grazie mille per la risposta...non ho capito bene tutto,ci penserò un po su 
grazie ancora

grazie ancora