DOMANDA Codice C++ class friend non funziona

Pubblicità
No, non funziona. Compila, ma la tua implementazione del metodo CambiaColore è completamente sbagliata.
E se avessi aggiunto stampaDati alla fine del main te ne saresti accorto subito.
PS guarda il codice di @DispatchCode
già, infatti l' ho dovuto togliere dal main....
Sono concetti base della OOP questi. L'oggetto Punto istanziato nel main non è l'oggetto Punto che istanzi nella funzione, sono appunto due istanze diverse.
Scusa poi: secondo te, perchè passare il riferimento all'oggetto al metodo CambiaColori? Non è solo per far compilare il codice...
L' obbiettivo principale era quello di passare un valore alla funzione CambiaColore che poi a sua volta avrebbe passato il valore alla funzione StampaDati facendo apparire il valore in output
Che poi a dire il vero quello è un concetto generale di programmazione, non solo di OOP e nemmeno del linguaggio C. È un concetto base.
--- i due messaggi sono stati uniti ---
Domanda a @regedit : quanti anni hai, che scuola frequenti e quale è il tuo libro di testo. Ok, sono tre domande :)
beh, sono domande particolari. Vado per i 37 anni, lavoro in una pizzeria e avendo sempre avuto la passione per videogiochi e computer ho deciso (tardamente) di iniziare ad affrontare il discorso programmazione per un obbiettivo futuro. Al momento tutto quanto come autodidatta studiando il libro "C e C++ le chiavi della programmazione" di Carlo A. Mazzone. Ho iniziato a studiare da Gennaio.
 
No, il metodo CambiaColore deve solo cambiare il colore al Punto che viene passato come parametro. Spetta poi al main() di chiamare StampaDati cosa che può fare essendo un metodo pubblico.
Quello che tu hai fatto è che CambiaColore cambia il colore di una classe dichiarata localmente, ma non cambia il colore della classe dichiarata nel main()

comunque complimenti, se hai iniziato solo a gennaio hai fatto parecchi passi, ma ti mancano ancora molte basi. Io ti consiglierei di tornare a un bel ripasso sulla programmazione funzionale, lasciando per il momento da parte quella ad oggetti, per cui non sei ancora pronto.
 
Mi associo ai complimenti allora! Anche per la voglia di iniziare a studiare qualcosa partendo da 0, non essendo più un ventenne.

Ad ogni modo anche se la funzione CambiaColore non fosse stata "friend" sarebbe stato comunque errato richiamare da lì la stampa dei dati (errato da un punto di vista logico). Questo perchè già il nome stesso della funzione fa capire che quella si occupa solo di settare il colore, e poi perchè non è detto che se riutilizzassi quella classe avresti bisogno dell'output (stampa dati).
 
No, il metodo CambiaColore deve solo cambiare il colore al Punto che viene passato come parametro. Spetta poi al main() di chiamare StampaDati cosa che può fare essendo un metodo pubblico.
Quello che tu hai fatto è che CambiaColore cambia il colore di una classe dichiarata localmente, ma non cambia il colore della classe dichiarata nel main()

comunque complimenti, se hai iniziato solo a gennaio hai fatto parecchi passi, ma ti mancano ancora molte basi. Io ti consiglierei di tornare a un bel ripasso sulla programmazione funzionale, lasciando per il momento da parte quella ad oggetti, per cui non sei ancora pronto.
Grazie, infatti ho già in programma di prendere anche altri libri per ripassare al meglio ogni cosa imparata dove ho ancora grandi lacune, soprattutto mi servirebbe uno dove ci sono esercizi pratici.
Tornando a noi, una cosa non mi è chiara; perchè sarebbe corretto istanziare Punto P dentro il main invece che all' interno della funzione CambiaColore? Quello che non capisco è per il fatto che come parametri di funzione ci sia il riferimento "&p" e nel corpo della funzione ci sia solo p.colore senza nessuna inizializzazione. Ho infatti sempre notato, nel libro che leggo, che le inizializzazioni vengono fatte spesso dentro il corpo della funzione...
--- i due messaggi sono stati uniti ---
Mi associo ai complimenti allora! Anche per la voglia di iniziare a studiare qualcosa partendo da 0, non essendo più un ventenne.

Ad ogni modo anche se la funzione CambiaColore non fosse stata "friend" sarebbe stato comunque errato richiamare da lì la stampa dei dati (errato da un punto di vista logico). Questo perchè già il nome stesso della funzione fa capire che quella si occupa solo di settare il colore, e poi perchè non è detto che se riutilizzassi quella classe avresti bisogno dell'output (stampa dati).
Di fatti come ho letto il codice la prima volta mi faceva strano anche a me. Ancora più strano che si sia ripetuta la StampaDati nel main!! ed è da lì che ho intuito che ci sarebbe stato qualcosa che mi avrebbe bloccato :D
 
Tornando a noi, una cosa non mi è chiara; perchè sarebbe corretto istanziare Punto P dentro il main invece che all' interno della funzione CambiaColore? Quello che non capisco è per il fatto che come parametri di funzione ci sia il riferimento "&p" e nel corpo della funzione ci sia solo p.colore senza nessuna inizializzazione.

Perchè quando richiami la funzione CambiaColore passi già un oggetto di tipo Punto. Per essere precisi è un passaggio per riferimento; il riferimento "punta" alla memoria allocata per quell'oggetto.
Dovresti guardare i puntatori in C, il concetto è praticamente il medesimo.

Non è complesso concettualmente, vale anche per le variabili. Quando passi una variabile (magari di tipo int) per riferimento, stai passando l'indirizzo di quella variabile. Il valore della variabile, il numero che gli assegni per capirci, viene salvato all'indirizzo in cui si trova la variabile.
Per fare un esempio stupido:

Codice:
int a = 100; // Supponiamo che l'indirizzo sia 0x1234 (e.g. è espresso in esadecimale per comodità)

quando passi per riferimento questa variabile, viene passato l'indirizzo 0x1234. Quindi l'argomento della funzione riceve questo indirizzo e se apporta delle modifiche al valore della variabile (il nr. 100 dell'esempio) questo cambia anche per la funzione chiamante, perchè l'indirizzo a cui puntano è il medesimo.

Per spiegarmi meglio, considera questo esempio:
C++:
#include <iostream>

using namespace std;

void func(int &n, int m) {
    cout << "Indirizzo di N in func() << " << &n << endl;
    cout << "Indirizzo di M in func() << " << &m << endl;
    n *= 2;
    m *= 2;
}


int main() {
    int n = 10;
    int m = 10;
    cout << "Valore di N: " << n << " indirizzo: " << &n << endl;
    cout << "Valore di M: " << m << " indirizzo: " << &m << endl;
    
    func(n, m);
    
    cout << "N dopo moltiplicazione: " << n << "\nM dopo la moltiplicazione: " << m;
    
    return 0;
}
Puoi eseguirlo direttamente da qui se preferisci: http://cpp.sh/8z7fr

Output:
Codice:
Valore di N: 10 indirizzo: 0x71b09fa8f7f8 
Valore di M: 10 indirizzo: 0x71b09fa8f7fc 
Indirizzo di N in func() 0x71b09fa8f7f8 
Indirizzo di M in func() 0x71b09fa8f7cc 

N dopo moltiplicazione: 20 
M dopo la moltiplicazione: 10

Guarda gli indirizzi: l'indirizzo della variabile N è uguale sia nel main() che nella funzione func(), mentre quello di M è diverso. Infatti noterai che il valore di N è stato moltiplicato per 2, mentre quello di M è rimasto uguale a prima.

Con gli oggetti è un pò più diverso, perchè quando crei un oggetto stai allocando della memoria nell'heap. In buona sostanza ti viene comunque restituito un puntatore a questa memoria. Ogni volta che crei un oggetto viene allocata della nuova memoria, in una locazione diversa da prima quindi, e viene restituito un puntatore a questa memoria.
Il concetto di passaggio per riferimento però è lo stesso, si tratta sempre del passaggio dell'indirizzo di memoria.

Quindi tornando al tuo caso: la variabile p del main e quella della funzione CambiaValore puntano alla medesima memoria, quindi se fai una modifica su quell'oggetto, questa modifica si vede anche nella funzione chiamante (ovvero nel main()).
Se crei un nuovo oggetto in CambiaColore stai effettivamente creando un altro oggetto, che quindi punterà a della memoria divesa dall'altro, e il tuo valore nel main sull'oggetto p rimarrà quello di prima.

Sono andato un pò di fretta, spero di essere stato chiaro.
 
Tornando a noi, una cosa non mi è chiara; perchè sarebbe corretto istanziare Punto P dentro il main invece che all' interno della funzione CambiaColore? Quello che non capisco è per il fatto che come parametri di funzione ci sia il riferimento "&p" e nel corpo della funzione ci sia solo p.colore senza nessuna inizializzazione.
perché nel main() hai creato una classe Punto, quindi quando chiami CambiaColore vuoi che quel metodi cambi il colore di quella classe, e non quello di una classe dichiarata altrove (nel tuo caso nel metodo CambiaColore. Ecco perché uno dei parametri di CambiaColore è propio una classe Punto (che è già inizializzata).
Uno dei motivi per cui scriviamo metodi (o funzioni, come in questo caso) è proprio perché vogliamo eseguire operazioni su variabili che noi abbiamo già dichiarato.

l’uso del simbolo & nella dichiarazione del parametro è indispensabile perché vogliamo passare il parametro per riferimento, in modo che possa essere cambiato all’interno della funzione. Questo è tipico del linguaggio C, in altri linguaggi si usano altri simboli ma il concetto è lo stesso. Se il parametro viene passato “per valore”, la funzione crea un “copia” locale di tale parametro, può cambiarlo ma rimane un cambiamento appunto locale, quando il metodo ritorna il parametro nella fu zio e chiamante rimane inalterato. Se invece il parametro viene passato “per riferimento”, la funziona non fa nessuna copia, ma opera sullo stesso parametro, qualsiasi cambiamento che ne fa risulta poi presente nella funzione chiamante.

questo è uno dei concetti base della programmazione, ti consiglio di ripassarlo a dovere e fare esercizi. Cerca nel tuo libro nel capitolo che parla come passare i parametri a una funzione.
--- i due messaggi sono stati uniti ---
Tanto per aiutarti ho fatto una veloce ricerca Google in italiano e ho trovato questa Pagina che spiega abbastanza bene il concetto anche con esercizi (che ti consiglio di fare, si impara solo facendo esercizi). Negli esempi vengono usati variabili di tipo intero, ma è lo stesso per qualsiasi tipo di dati, incluso classi.
 
Perchè quando richiami la funzione CambiaColore passi già un oggetto di tipo Punto. Per essere precisi è un passaggio per riferimento; il riferimento "punta" alla memoria allocata per quell'oggetto.
Dovresti guardare i puntatori in C, il concetto è praticamente il medesimo.

Non è complesso concettualmente, vale anche per le variabili. Quando passi una variabile (magari di tipo int) per riferimento, stai passando l'indirizzo di quella variabile. Il valore della variabile, il numero che gli assegni per capirci, viene salvato all'indirizzo in cui si trova la variabile.
Per fare un esempio stupido:

Codice:
int a = 100; // Supponiamo che l'indirizzo sia 0x1234 (e.g. è espresso in esadecimale per comodità)

quando passi per riferimento questa variabile, viene passato l'indirizzo 0x1234. Quindi l'argomento della funzione riceve questo indirizzo e se apporta delle modifiche al valore della variabile (il nr. 100 dell'esempio) questo cambia anche per la funzione chiamante, perchè l'indirizzo a cui puntano è il medesimo.

Per spiegarmi meglio, considera questo esempio:
C++:
#include <iostream>

using namespace std;

void func(int &n, int m) {
    cout << "Indirizzo di N in func() << " << &n << endl;
    cout << "Indirizzo di M in func() << " << &m << endl;
    n *= 2;
    m *= 2;
}


int main() {
    int n = 10;
    int m = 10;
    cout << "Valore di N: " << n << " indirizzo: " << &n << endl;
    cout << "Valore di M: " << m << " indirizzo: " << &m << endl;
   
    func(n, m);
   
    cout << "N dopo moltiplicazione: " << n << "\nM dopo la moltiplicazione: " << m;
   
    return 0;
}
Puoi eseguirlo direttamente da qui se preferisci: http://cpp.sh/8z7fr

Output:
Codice:
Valore di N: 10 indirizzo: 0x71b09fa8f7f8
Valore di M: 10 indirizzo: 0x71b09fa8f7fc
Indirizzo di N in func() 0x71b09fa8f7f8
Indirizzo di M in func() 0x71b09fa8f7cc

N dopo moltiplicazione: 20
M dopo la moltiplicazione: 10

Guarda gli indirizzi: l'indirizzo della variabile N è uguale sia nel main() che nella funzione func(), mentre quello di M è diverso. Infatti noterai che il valore di N è stato moltiplicato per 2, mentre quello di M è rimasto uguale a prima.

Con gli oggetti è un pò più diverso, perchè quando crei un oggetto stai allocando della memoria nell'heap. In buona sostanza ti viene comunque restituito un puntatore a questa memoria. Ogni volta che crei un oggetto viene allocata della nuova memoria, in una locazione diversa da prima quindi, e viene restituito un puntatore a questa memoria.
Il concetto di passaggio per riferimento però è lo stesso, si tratta sempre del passaggio dell'indirizzo di memoria.

Quindi tornando al tuo caso: la variabile p del main e quella della funzione CambiaValore puntano alla medesima memoria, quindi se fai una modifica su quell'oggetto, questa modifica si vede anche nella funzione chiamante (ovvero nel main()).
Se crei un nuovo oggetto in CambiaColore stai effettivamente creando un altro oggetto, che quindi punterà a della memoria divesa dall'altro, e il tuo valore nel main sull'oggetto p rimarrà quello di prima.

Sono andato un pò di fretta, spero di essere stato chiaro.
Dunque, vediamo se ho capito bene la tua spiegazione. Hai elaborato la variabile n per riferimento alla funzione ed avendo sempre lo stesso valore in allocazione di memoria mi fai capire che nel mio codice p = n cioè la tua "n" era l' esempio per rispondere alla mia domanda sulla variabile "p". Forse dico una cavolata, ma nel caso io avessi posto la sintassi Punto P dentro la funzione CambiaColore (Punto &a, int c) al posto di inserirla nel main si sarebbe creato uno "scollegamento" tra allocazioni di memoria impedendo così la corretta compilazione? o ci sarebbe stato un ovverride? Un' altra cosa che non riesco a capire del tuo ultimo codice è l'operatore *=
che funzione svolge? se è una semplice moltiplicazione perchè usare anche l' uguale? purtroppo nel libro questa cosa non c'è :/
 
Il simbolo *= rappresenta un “operatore composto”, puoi trovare la sua definizione per esempio Qui. Sono estremamente comodi.
Mi pare strano non sia presente nel tuo libro, dovrebbe essere proprio all”inizio quando parla degli operatori.

ti consiglio di guardare tra le discussioni in rilievo, ce ne è una proprio dedicata ai libri e manuali.
 
Il simbolo *= rappresenta un “operatore composto”, puoi trovare la sua definizione per esempio Qui. Sono estremamente comodi.
Mi pare strano non sia presente nel tuo libro, dovrebbe essere proprio all”inizio quando parla degli operatori.

ti consiglio di guardare tra le discussioni in rilievo, ce ne è una proprio dedicata ai libri e manuali.
Lascia stare il mio libro, ho scoperto tardi che da spiegazioni superficiali e non approfondisce argomenti che meritano paragrafi su paragrafi. Tutto sommato conosco già gli operatori aritmetici quelli di incremento e decremento e in più quelli booleani, ma nient'altro.

quindi dispatch con n *= 2 avrebbe come risultato 10 = 10 *(2+1), ma non fa 30?...

P.S.: avete dei libri con esercizi da consigliarmi (sempre per principianti) sia del C che del C++?
poi guardo anche quel sito che mi ha consigliato Andretti
 
No, n*=2; è la forma compatta di n = n*2, è per non ripetere la medesima variabile; un pò come n++ è la forma compatta di n = n+1. Ovviamente puoi farlo con tutti gli operatori, come dice Andretti: quindi lo stesso discorso vale per n /= 2; che è n = n/2 o anche con gli operatori bit a bit, tipo n &= 2; che è come n = n & 2;.

Dunque, vediamo se ho capito bene la tua spiegazione. Hai elaborato la variabile n per riferimento alla funzione ed avendo sempre lo stesso valore in allocazione di memoria mi fai capire che nel mio codice p = n cioè la tua "n" era l' esempio per rispondere alla mia domanda sulla variabile "p". Forse dico una cavolata, ma nel caso io avessi posto la sintassi Punto P dentro la funzione CambiaColore (Punto &a, int c) al posto di inserirla nel main si sarebbe creato uno "scollegamento" tra allocazioni di memoria impedendo così la corretta compilazione? o ci sarebbe stato un ovverride? Un' altra cosa che non riesco a capire del tuo ultimo codice è l'operatore *=
che funzione svolge? se è una semplice moltiplicazione perchè usare anche l' uguale? purtroppo nel libro questa cosa non c'è :/

Si nel mio esempio n era per farti capire il passaggio per riferimento su un tipo di dato più semplice, come gli interi.
Prova a leggere anche questa risorsa, magari ti chiarisce le cose https://www.cs.fsu.edu/~myers/c++/notes/references.html
Ah, non so che rapporto hai con l'inglese, non mi ricordo/non ho visto se l'hai detto da qualche altra parte, ma abituati a cercare risorse in inglese a leggere cose in inglese.

Provo a rispiegarmi, ma leggi anche quella risorsa. Poi se hai domande chiedi pure qui.
Torniamo al tuo esempio dell'oggetto: ni, nel senso che stai allocando memoria per 2 oggetti diversi. Quando crei il primo oggetto di tipo Punto viene riservata della memoria e l'indirizzo a quella memoria viene assegnato a p.
Nella funzione tu allocavi nuovamente memoria, quindi veniva riservata nuova memoria, dandoti un indirizzo diverso. Ergo le due aree di memoria e le modifiche fatte su quegli oggetti, non hanno nulla in comune.

Considera che non potrai afferrare tutti i concetti "subito". Per alcune cose occorre del tempo, perchè "pescano" da aree non direttamente connesse al linguaggio in sè. Quindi proseguendo con lo studio del linguaggio in sè e in generale di altri concetti (l'allocazione dinamica è gestita dal sistema operativo, ad esempio) ti sarà tutto più chiaro.
Soprattutto: tanta pratica. Esegui l'esempio che ti ho mostrato sopra, modificalo, e prova a capire i concetti basilari a fondo; come il passaggio per riferimento di una variabile, e quindi la differenza tra "pass by reference vs. pass by value".
 
Grazie ragazzi! Stavolta la spiegazione di @DispatchCode l' ho capita al volo :D anche perchè un po immaginavo fosse così.
Tuttavia non mi torna ancora quell' operatore come funziona. Cioè @DispatchCode si è spiegato benissimo però se leggo qui e confronto il tuo n *= 2 ( n = n*2) non combacia. Nel link dice che si fa n = n* (2+1) quindi 10 = 10*(3)...
 
La parte che devi guardare tu è questa:

Operatori di assegnamento composti ( +=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |= )
Gli operatori di assegnamento composti sono una caratteristica del C++ che contribuisce alla sua fama di essere un linguaggio sintetico. Essi permettono di modificare il valore di una variabile con una sola operazione:
valore += incremento; è equivalente a valore = valore + incremento;
a -= 5;
è equivalente ad a = a - 5;
a /= b;
è equivalente ad a = a / b;
prezzo *= numero + 1;
è equivalente a prezzo = prezzo * (numero + 1);
e analogamente per le altre operazioni.

Fa più o meno gli esempi che ti ho fatto sopra. Non so dove hai visto l'esempio che hai riportato.
Cosa ti crea dubbi in questa parte?
 
valore += incremento; è equivalente a valore = valore + incremento;
a -= 5; è equivalente ad a = a - 5;
a /= b; è equivalente ad a = a / b;
prezzo *= numero + 1; è equivalente a prezzo = prezzo * (numero + 1);

n *= 2+1;
10 = 10* (2+1)
10 = 10*(3)


nel tuo caso il prezzo sarebbe n e il numero sarebbe 2 che va sommato a 1
 
Ma no, perchè scusa? Il mio esempio è identico a a /= b;, dove al posto di 'a' c'è la n e al posto di b c'è 2.
Quello che riporti in grassetto è un esempio diverso e "più complesso":

Codice:
int n = 10;
int m = 5;

n *= m +1;  // corrisponde a "n = n * (m+1);"  ->  n = 10 * (5 + 1) = 60

prezzo e numero sono due variabili distinte e 1 è una costante numerica.
Il mio esempio è come il primo (e il secondo) tra quelli riportati. Nel mio esempio hai n e poi un valore noto (che è 2), in quello hai due valori non noti, "a" e "b" (nel primo esempio hai in realtà una costante numerica, come nel mio esempio).
Che siano due variabili con un valore qualsiasi o che invece di una variabile ci sia un valore numerico, non cambia nulla, l'operazione è sempre quella.

Inoltre sono problemi ai quali puoi risponderti direttamente eseguendo il codice. Quando hai un dubbio la risposta più rapida per capire come funziona è provare.

C++:
#include <iostream>

using namespace std;

int main()
{
    int n = 10;
    n *= 2;
    cout << "N: " << n << endl;
    
    int a = 10, b = 2;
    a *= b + 1;
    cout << "A: " << a << endl;
    
    return 0;
}

Per eseguire online http://cpp.sh/6klww

Output:
Codice:
N: 20
A: 30
 
Pubblicità
Pubblicità
Indietro
Top