Newsgroup: it.comp.lang.c++
Titolo: Valori di ritorno

Sto scrivendo un programma per un progetto di Fondamenti II. Siccome
il corso si è concentrato sulla programmazione OOP e siccome vorrei
uilizzare questa tecnologia al meglio, vorrei alcuni pareri su una
strategia che ho intenzione di adottare.
Supponete di avere una classe A. Tale classe è formata da una serie di
proprietà private (lo stato dell'oggetto) che io posso variare
utilizando opportune funzioni di accesso (la tipica coppia SetX/GetX).
In qualunque istante della vita dell'istanza infatti, vorrei che
questa fosse sempre valida, nel senso che è sempre possibile, in
qualsiasi momento esguire una qualsiasi operazione su di essa e che
risponda alle norme del buon comportamento (rilascio di risorse, non
creazione di aree di memoria orfane ecc.).
Fin qui tutto bene. Supponete però di avere una classe Grafo che
contiene, ovviamente, una seria di istanze di classi Nodo ed Arco.
Supponiamo poi che dall'esterno, io voglia accedere ad uno di questi
nodi (che contiene in se anche le informazioni sulle connessioni agli
altri nodi del grafo). Allora, una ipotetica funzione GetNode ecc.,
che cosa dovrebbe ritornare ? Una copia o un riferimento al nodo ?
Entrambi i casi presentano dei problemi. Il secondo ovviamente sotto
il punto di vista della sicurezza : il nodo risulta sguarnito ad
interventi esterni che quindi possono alterarne il contenuto che, pur
rimanendo legale per un nodo potrebbe invalidare il contenuto del
grafo (ad esempio cambiando gli archi).
D'altro canto l'uso del primo impedirebbe la creazione di algoritmi
particolari di visita del grafo (ad esempio in situazione di
subclassing) perchè sicuramente i riferimenti agli altri nodi
sarebbero esclusi dalla copia del nodo (il nodo non conterrebbe in
pratica informazioni relative al grafo che lo contiene, presenti
invece nell'originale). Inoltre questo approccio è molto pesante, in
quanto prevede la creazione di nuove istanza nel ritorno dei
parametri, che sicuramente si rivela costosa in presenza di oggetti
molto complicati.
Che ne dite ? Quale approccio mi consigliate ?
Grazie !

 [...]
Scusa ma non ho capito bene cosa intendi per _contiene_,se e' una cosa
del tipo:
class Grafo
{
...
private:
   nodi * Nodo;
   archi * Archi;
};
Puoi rendere privati i membri SetX di Nodo e scrivere:
class Grafo
{
public:
friend class Nodo;
void InsertNodo(...);
void SetXNodo(...);
Nodo& GetNodo(...);
...
private:
   nodi * Nodo;
   archi * Archi;
};
Chiaramente non tutte le SetXNodo vanno rese disponibili per muovere un
nodo non si deve procedere a mano ma con una:
void Grafo::MoveNodo(...);
In modo da garantire la consistenza della struttura.
Ciao.
-- 
     (ZZZ)       Filippo Salotti, Bagni di Lucca, Lucca, Italy.
    //   \\  _                 E-mail : 
fil...@humnet.unipi.it
   /Y\    ZD<_o===============================================o
 .oZZZZZo.     | Studente Ingegneria Informatica Automatica  |
<OXXXXXXXO>    | Dip. Sistemi Elettrici Universita' di Pisa. |

molto complicati.
E quindi da scartare...
Una alternativa che mi viene in mente e` quella di rendere accessibili
direttamente  Nodi e Archi alla classe grafo e ad una (in realta anche due)
classe proxy che permetterebbe l'esecuzione a terzi di operazioni controllate.
In sostanza la classe proxy dovrebbe esportare i metodi di Nodi 
strettamente necessari e che possibilmente non consentano la modifica
del Nodo. Questo da una parte diminuirebbe l'accoppiamento tra classe
Grafo e classi Arco e Nodi. Ovviamente ci sarebbe un forte accoppiamento tra
classi Arco e Nodo con le rispettive classi proxy, ma questo mi sembra intrinseco nel
problema e comunque una differente implementazione di arco e nodo avrebbero minor
impatto sulla classe grafo.
Attenzione e` un'idea che mi viene in mente al momento e sicuramente necessita di
ulteriore analisi.
Saluti,
Paolo
----------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
  e-mail: 
mar...@cli.di.unipi.it
 
----------------------------------------------------------

che cosa dovrebbe ritornare ? Una copia o un riferimento al nodo ?
Normalmente, un riferimento costante (const Nodo&); non c'e` ragione
di duplicare informazioni gia` presenti.  Nulla, naturalmente, impedisce
al "codice cliente" di rimuovere la protezione con const_cast, ma d'altra
parte l'intero sistema di protezione dall'accesso in C++ e` "basato sulla
fiducia": e` fatto per proteggere contro *incidenti*, e del tutto inadeguato
ove si dovesse sospettare deliberate violazioni da parte del codice
cliente.
Piu` elegante, generale, scalabile, ecc, e` passare un riferimento const,
non al Nodo (presumibilmente una classe concreta), ma alla *interfaccia*
(base puramente astratta, o "protocol class" nel senso di Lakos che dir
si voglia) che vuoi che questo nodo presenti all'esterno; in questo modo,
eviti di creare inutili dipendenze del codice-cliente da dettagli d'implementazione
delle classi del componente, che possono benissimo cambiare.  Ad
esempio:
// il file .h visibile al codice-cliente comprende:
    template <typename Contenuto>
    class INodo {    // l'interfaccia
    public:
        virtual Contenuto& getContenuto() = 0;
        virtual const Contenuto& getContenuto() const = 0;
        virtual std::list<const INodo*> successori() const = 0;
        virtual std::list<const INodo*> predecessori() const = 0;
    };
// il file .h interno all'implementazione #include il precedente
// e prosegue con:
    template <typename Contenuto>
    class Nodo: public INodo<Contenuto> {
        Contenuto c;
        std::list<Nodo*> succ;
        std::list<Nodo*> pred;
        typedef INodo<Contenuto> Interf;
    public:
        virtual Contenuto& getContenuto() {
            return c;
        }
        virtual const Contenuto& getContenuto() const {
            return c;
        }
        virtual std::list<const Interf*> successori() const {
            std::list<const Interf*> result;
            std::list<Nodo*>::const_iterator it;
            for(it=succ.begin(); it!=succ.end(); ++it)
                result.push_back(*it);
            return result;
        }
        virtual std::list<const Interf*> predecessori() const {
            std::list<const Interf*> result;
            std::list<Nodo*>::const_iterator it;
            for(it=pred.begin(); it!=pred.end(); ++it)
                result.push_back(*it);
            return result;
        }
        // altre funzioni, ad es. modificatori, che non vuoi
        // rendere accessibili al codice-cliente
    };
(Puo` darsi che il tuo compilatore attualmente non ti permetta
di usare template attraverso il confine fra componenti, nel qual
caso, naturalmente, dovrai rimuovere la pur comoda natura
"template" di questo esempio ed istanziare solo casi specifici).
Alex