Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Funz. operatore che ritorna un reference

1 view
Skip to first unread message

nembo kid

unread,
Jul 10, 2008, 8:38:01 AM7/10/08
to
Ho questa funzione di overload di operatore:


////////////////////////////////////////////////////
// Array è una classe per creare, gestire vettori:

class Array {

//...

public:

const int &Array::operator[] ( int subscript ) const
{
return ptr[ subscript ];
}

private:
int *ptr; // points to the first element of array
};

////////////////////////////////////////////////////


Non arrivo a capire una cosa.

Per me ptr[subscript] dovrebbe essere un intero, perché allora la
funzione non è:

const int Array::operator[] ( int subscript ) const


???

Grazie

Andrea Laforgia

unread,
Jul 10, 2008, 9:10:23 AM7/10/08
to
On 10 Lug, 14:38, nembo kid <nembo@kid> wrote:

> Non arrivo a capire una cosa.
>
> Per me ptr[subscript] dovrebbe essere un intero

No, un reference ad intero. Se fosse solo un intero, non otterresti un
l-value e non potresti quindi assegnare un valore tramite subscript. E
comunque devi togliere quel const.

GM

unread,
Jul 10, 2008, 9:38:55 AM7/10/08
to
nembo kid ha scritto:

In questo caso (siccome la funzione è const) in effetti la versione che
ritorna il valore è equivalente a quella che ritorna il riferimento.
Anzi, potrebbe anche essere un pelino più efficiente siccome l'int è
piccolo (ma probabilmente con un buon compilatore non cambia nulla).

Un motivo per mantenere il ritorno col reference, può essere quello di
"omogeneità" con la versione non cost, che nell'esempio che hai messo tu
non c'è ma in generale (a meno ché il concetto di "Array" che vuoi
esprimere sia immutabile) è presente:

const int &Array::operator[] ( int subscript ) const
{
return ptr[ subscript ];
}
int &Array::operator[] ( int subscript )

{
return ptr[ subscript ];
}


E nota bene che la versione non const (come ti è già stato fatto notare)
DEVE tornare un reference per poter essere l-value (cioè poter stare a
sx di un assegnamento)

Occhio che se invece che int l'array fosse di roba più grossa (o ad
esempio di un tipo T generico argomento di template) la versione col
reference sarebbe anche + efficiente (si risparmia la copia).


GM

nembo kid

unread,
Jul 10, 2008, 9:50:20 AM7/10/08
to
GM ha scritto:


> E nota bene che la versione non const (come ti è già stato fatto notare)
> DEVE tornare un reference per poter essere l-value (cioè poter stare a
> sx di un assegnamento)


Infatti, il Deitel le mette due const e non const, rispettivamente per
creare un rvalue e un lvalue.

Nicola Musatti

unread,
Jul 10, 2008, 12:48:07 PM7/10/08
to

E' preferibile cosi', perche' e' sempre meglio evitare di ritornare
reference (cosi' come ritornare puntatori) quando si puo'.

Tra l'altro la "promessa" che si fa ritornando un reference e'
decisamente piu' forte di quella che si fa ritornando un puntatore;
mentre i puntatori per definizione possono essere validi, nulli o non
validi, i reference si assume che siano sempre validi. Ritornando un
reference quindi in un certo qual modo ti impegni a mantenere valido
l'oggetto riferito a tempo indeterminato.

Ciao,
Nicola

Davide Quack

unread,
Jul 11, 2008, 3:57:51 AM7/11/08
to
Nicola Musatti ha detto questo giovedì :

> Ritornando un
> reference quindi in un certo qual modo ti impegni a mantenere valido
> l'oggetto riferito a tempo indeterminato.

Questo no. Da questo punto di vista non vedo differenza con un
puntatore. Anche se ritorni un puntatore non è bello se l'oggetto
puntato diventa improvvisamente non valido.

Ma ha poi senso pensare questo? La stringa ha la funzione c_str() che
torna un puntatore costante, ma non rende di certo la stringa
immutabile, e quindi il puntatore "sempre" valido. L'operatore [], o di
assegnazione è ragionevole che torni un reference, ma è anche
ragionevole che non _fissi_ l'oggetto referenziato.

Se poi uno ci tiene a cercarsi rogne a tutti i costi, allora il C++ è
il linguaggio che fa per lui:

std::vector<std::string> vect;

vect.push_back("Hello");
vect.push_back("Duck");

size_t i = vect.size();
while(i--)
{
const std::string& s = vect[i];
vect.pop_back();
const char * p = s.c_str();
// per motivi ignoti a me con vc2005 ho che p == 0
}

--
Articolo 33 della Costituzione italiana: L'arte e la scienza sono
libere e libero ne è l'insegnamento.
detto qesto: http://www.pluto.it/files/ildp/HOWTO/Coffee/Coffee.html
oppure questo? http://lenr-canr.org/acrobat/DeNinnoAexperiment.pdf


Nicola Musatti

unread,
Jul 11, 2008, 4:34:31 AM7/11/08
to
On Jul 11, 9:57 am, Davide Quack <qu...@tin.it> wrote:
> Nicola Musatti ha detto questo giovedì :
>
> > Ritornando un
> > reference quindi in un certo qual modo ti impegni a mantenere valido
> > l'oggetto riferito a tempo indeterminato.
>
> Questo no. Da questo punto di vista non vedo differenza con un
> puntatore. Anche se ritorni un puntatore non è bello se l'oggetto
> puntato diventa improvvisamente non valido.

Per carita', non volevo dire questo. Piuttosto se a uno piove un
puntatore dal cielo a priori non sa se e' valido o no e l'informazione
gli deve arrivare in un'altra forma (tipicamente per convenzione), se
invece gli piove un reference ha tutte le regioni di fidarsi della sua
validita'.

> Ma ha poi senso pensare questo? La stringa ha la funzione c_str() che
> torna un puntatore costante, ma non rende di certo la stringa
> immutabile, e quindi il puntatore "sempre" valido.

Infatti e' accompagnata da precisazioni molto circostanziate sulla sua
"finestra di validita'".

> L'operatore [], o di
> assegnazione è ragionevole che torni un reference, ma è anche
> ragionevole che non _fissi_ l'oggetto referenziato.

Per l'operatore di assegnamento faccio un' eccezione, nel senso che
non trovo niente di male a ritornare un reference all'oggetto su cui
e' stata chiamata la funzione membro: in fondo qualsiasi ragionamento
sulla validita' dell'oggetto deve valere a monte della chiamata
stessa.

In generale non mi piace ritornare per reference un sotto-oggetto di
un oggetto piu' complesso, ma la metafora dell'array e'
irrinunciabile.

> Se poi uno ci tiene a cercarsi rogne a tutti i costi, allora il C++ è
> il linguaggio che fa per lui:
>
> std::vector<std::string> vect;
>
> vect.push_back("Hello");
> vect.push_back("Duck");
>
> size_t i = vect.size();
> while(i--)
> {
> const std::string& s = vect[i];
> vect.pop_back();
> const char * p = s.c_str();
> // per motivi ignoti a me con vc2005 ho che p == 0
>
> }

Vien da pensare che il distruttore di std::string, scatenato dalla
chiamata a pop_back(), riporti l'oggetto ad uno stato iniziale valido.
Si comporta allo stesso modo se compili in modo debug o in release? VC+
+2005 ha anche il controllo sullo sfondamento dei buffer se non
ricordo male, mi sembrava ci fosse qualche macro da definire per
abilitarlo/disabilitarlo.

Ciao,
Nicola

Davide Quack

unread,
Jul 11, 2008, 5:00:16 AM7/11/08
to
Nicola Musatti scriveva il 11/07/2008 :

> [..] Piuttosto se a uno piove un


> puntatore dal cielo a priori non sa se e' valido o no e l'informazione
> gli deve arrivare in un'altra forma (tipicamente per convenzione), se
> invece gli piove un reference ha tutte le regioni di fidarsi della sua
> validita'.

Su questo sono completamente d'accordo.
Avevo travisato il tuo pensiero.

> Vien da pensare che il distruttore di std::string, scatenato dalla
> chiamata a pop_back(), riporti l'oggetto ad uno stato iniziale valido.
> Si comporta allo stesso modo se compili in modo debug o in release?

Ho riscritto l'esempio così:

vect.push_back("Hello");
vect.push_back("Duck");

size_t i = vect.size();
while(i--)
{
const std::string& s = vect[i];
vect.pop_back();
const char * p = s.c_str();

printf("%s\n", p);
printf("%i\n", reinterpret_cast<int>(p));
}

Il puntatore punta sempre a qualcosa ad una locazione di memoria che
sembra sensata, e la stringa stampata è la stringa vuota. Non capisco.

Presumo che ci sia questo effetto perché l'allocatore standard del
runtime di MS non ha disallocato in realtà quell'area di memoria, ma
l'ha solo resa disponibile. Mi sembra una finezza avere un distruttore
che azzera l'area di memoria per tentare di prevenire, con un poco di
sedere, del codice così mal scritto.

Comunque ci mediterò sopra. Magari provo a vedere cosa fa il GCC la
prima volta che mi capita di aprire la macchina virtuale con linux.

> VC+
> +2005 ha anche il controllo sullo sfondamento dei buffer se non
> ricordo male, mi sembrava ci fosse qualche macro da definire per
> abilitarlo/disabilitarlo.

L'ho disabilitato, ma il risultato non è cambiato.

softwareEngineer

unread,
Jul 11, 2008, 5:07:03 AM7/11/08
to

Lo dico tanto per precisare,
l-value non significa strettamente "poter stare a sinistra di un
assegnamento"
era originariamente così ... lo standard indica l-value come "localtion
value"
ovvero un espressione con la quale è possibile riferirsi ad un oggetto ...

lo dico per i nuovi di questo linguaggio che prendono per buono tutto quello
che viene detto :-)

by.

0 new messages