Autore Topic: [C++] overload dell'operator[]  (Letto 2308 volte)

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
[C++] overload dell'operator[]
« il: 13 Novembre 2011, 17:53:15 »
Ciao a tutti,

volendo creare una classe container che consenta di mantenere gli elementi in base all'ordine di inserimento, stavo cercando di modificare il listato suggeritomi da TheKaneB (http://http://www.nonsoloamiga.com/forum/viewtopic.php?f=12&t=950).

Però, invece del metodo put (
Codice: [Seleziona]
T* put(K key, T value) ), pensavo di usare l'operator[] (array subscript) per fare in modo da poter accedere o scrivere sul mio container.
Ho tentato di effettuare un overload di tale operatore così:

Codice: [Seleziona]
T& operator[] ( const K& x )
  {
    T& result = myMap[x] = 4; //myMap is a std::map
    std::cout<<"Operatore [], passato KEY:"<<x<<endl;
    return result;
  }

il fatto è che quando istanzio il mio oggetto, poi vorrei usare tale operatore nella consueta maniera:

Codice: [Seleziona]
myContainer[key] = value;
alla funzione membro dell'operatore arriva il valore di key (x nella mia funzione), ma come fare a risualire anche al valore (valore che, per prova, nella mia funzione, vale 4)?
Avevo pensato che dovrei anche coinvolgere l'operator=, ma è corretto? (cmq non ci sono riuscito...)
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline Z80Fan

  • Administrator
  • Guru
  • *****
  • Post: 1671
  • Karma: +13/-2
    • Mostra profilo
    • http://z80fan.altervista.org
Re: [C++] overload dell'operator[]
« Risposta #1 il: 13 Novembre 2011, 22:48:09 »
(Scrivo senza avere fatto prove, quindi prendere ogni informazione con le pinze)

Citazione da: "clros"
Ho tentato di effettuare un overload di tale operatore così:

Codice: [Seleziona]
T& operator[] ( const K& x )
  {
    T& result = myMap[x] = 4; //myMap is a std::map
    std::cout<<"Operatore [], passato KEY:"<<x<<endl;
    return result;
  }

il fatto è che quando istanzio il mio oggetto, poi vorrei usare tale operatore nella consueta maniera:

Codice: [Seleziona]
myContainer[key] = value;
Prima di tutto, quell'assegnazione (T& result = myMap
  • = 4) mi sa molto da ambigua. :D


Cmq tutto quello che dovresti fare nella funzione è ritornare un riferimento all'elemento; quando hai fatto quello, è il compilatore che si arrangia a usarlo nel modo che serve (se leggerlo o scriverlo).
Banalmente:

Codice: [Seleziona]
int vettore[10];

int & operator[] ( const int &x )
{
    return &(vettore[x]);
}
Poi che sia un l-value o un r-value non dovrebbe essere un problema, perchè tanto il riferimento non è costante quindi puoi fare quello che vuoi.
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] overload dell'operator[]
« Risposta #2 il: 25 Novembre 2011, 23:42:56 »
Allora, questo è lo scheletro della mia classe:

Codice: [Seleziona]
template <typename K, typename V>
class Insert_Order_Map : public std::map<K,V>
{
 
};

Insert_Order_map<string,int> myMap;
myMap["primo"] = 1;
//...

In questa classe (derivata da std::map) vorrei fare in modo di intercettare l'operatore [] (quando inserisco un elemento nella mia mappa) in modo da aggiornare una lista (std::list o std::vector) che tenga memorizzati gli elementi secondo la sequenza di inserimento. Come fare? :shock:

Di sicuro dovrei sovrascrivere l'operatore[] ma non so come procedere :( (ovviamente non ho problemi per l'aggiornamento della lista che effettuerei nella riscrittura dell'operatore)

TheKaneB aveva suggerito un metodo put:

 
Citazione
T* put(K key, T value)
   {
      DictionaryType::iterator it = mDictionary.find(key);

      if (it == mDictionary.end() )
      {
         T* tmp = &(mDictionary[key] = value);
         mHistory->push_front(tmp);
         return tmp;
      }

Penso che vada bene, solo che dovrei riscriverlo nell'operatore[]
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] overload dell'operator[]
« Risposta #3 il: 26 Novembre 2011, 01:26:08 »
Ok, apparentemente ci sono riuscito (ho scopiazzato il codice direttamente dall'header <map>  :D

Codice: [Seleziona]
V& operator[]( const K&& key )
  {
    typename std::map<K,V>::iterator __i = find(key);
if (__i == std::map<K,V>::end()) //se non l'ho trovato...
{
      __i = insert(__i, std::make_pair(std::move(key), V()) );
    }
return (*__i).second;
  }

Ads quello che proprio non riesco a capire è la parte il secondo parametro del make_pair; praticamente cos'è quel V() ??? :think:
V nel mio caso è il nome di un tipo della mia classe template:

Codice: [Seleziona]
template <typename K, typename V>
class FJM_Map : public std::map<K,V>
{
 //...
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re: [C++] overload dell'operator[]
« Risposta #4 il: 26 Novembre 2011, 15:27:24 »
semplicemente crea una nuova entry nella mappa, nello slot di nome key (di tipo K) con il valore di default del tipo V (value).

Il valore di default è dato dal costruttore di default V().

Questo significa che quando scrivi V tmp = mappa["mianonna"], la variabile tmp avrà SEMPRE un valore definito.
Se l'entry "mianonna" non era presente nella mappa, allora ti verrà creata un'entry nuova con il suo valore di default.
Ecco perchè è sempre bene usare il metodo find() quando si fanno ricerche nelle mappe, altrimenti hai 2 effetti collaterali:
- inspiegabilmente l'elemento che cerchi sarà sempre presente (per forza, te lo crea al volo!)
- aumenta la memoria occupata senza motivo, facendo esplodere l'applicazione anche per delle semplici ricerche.

Quindi usa sempre il find() per trovare un elemento e [] soltanto quando vuoi inserirne uno.
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] overload dell'operator[]
« Risposta #5 il: 26 Novembre 2011, 16:29:43 »
OKKey!
Ma ads c'è un altro problema (l'ultimo?)
Questo è il codice della mia classe più il main:

Codice: [Seleziona]
template <typename K, typename V>
class FJM_Map : public std::map<K,V>
{
 public:

 V& operator[]( const K&& key )
  {
   //Come già riportato in altro post
  }

  typename std::map<K,V>::iterator cbegin() const
  {
        return *mHistory.cbegin();
  }

  typename std::map<K,V>::iterator cend() const
  {
      return *mHistory.cend();
  }

  private:
    std::list<typename std::map<K,V>::iterator> mHistory;
};

using namespace std;

int main()
{
    FJM_Map<string,int> myMap;

    myMap["1"] = 1;
    myMap["23"] = 23;
    myMap["-3"] = -3;
    myMap["23"] = 28;
    myMap["ultimo"] = 33;

  //  std::cout<<myMap.cbegin()->first;
    for (auto x = myMap.cbegin(); x != myMap.cend(); ++x)
        cout<<(*x).first <<"t"<<x->second<<endl;
     return 0;



Il mio scopo è quello di visualizzare il contenuto della std::list, invece che quello della mappa, sovrascrivendo i metodi cbegin() e cend() di std::map (in pratica passo al chiamante gli iteratori di inizio e di fine della lista invece che della mappa).

Ecco, il for, dopo aver visualizzato alcuni elementi della mappa, va in segmentation fault  :cry:
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re: [C++] overload dell'operator[]
« Risposta #6 il: 26 Novembre 2011, 16:46:04 »
evidentemente qualcuno degli elementi della lista punta chissà dove (magari a end() oppure non è inizializzato, oppure è un puntatore nullo).
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] overload dell'operator[]
« Risposta #7 il: 26 Novembre 2011, 16:53:13 »
Citazione da: "TheKaneB"
evidentemente qualcuno degli elementi della lista punta chissà dove (magari a end() oppure non è inizializzato, oppure è un puntatore nullo).

Certo...però è strano, la lista la aggiorno solo quando inserisco un elemento nella mappa:


Codice: [Seleziona]
V& operator[]( const K&& key )
  {
    typename std::map<K,V>::iterator __i = find(key);
if (__i == std::map<K,V>::end()) //se non l'ho trovato...
{
           __i = insert(__i, std::make_pair(std::move(key), V()) );
          mHistory.push_back(__i);
          std::cout<<"Sto inserendo: "<<key<<std::endl;
    }
return (*__i).second;
  }
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline TheKaneB

  • Human Debugger
  • *****
  • Post: 5292
  • Karma: +20/-23
    • Mostra profilo
    • http://www.antoniobarba.org
Re: [C++] overload dell'operator[]
« Risposta #8 il: 26 Novembre 2011, 16:58:46 »
Ora ricordo una cosa abbastanza subdola e bastarda: gli iteratori delle mappe, dal momento che si tratta di una struttura dati "ordinata", vengono rimescolati ad ogni inserimento, diventando di fatto non validi.
Quindi nella tua lista dovresti metterci un puntatore all'elemento Value, non un iteratore della mappa.

Altra cosa, in quel codice stai inserendo elementi vuoti dentro la lista. Se ti funzionano gli assegnamenti è solo per pura fortuna.
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] overload dell'operator[]
« Risposta #9 il: 26 Novembre 2011, 17:13:24 »
Citazione da: "TheKaneB"
Ora ricordo una cosa abbastanza subdola e bastarda: gli iteratori delle mappe, dal momento che si tratta di una struttura dati "ordinata", vengono rimescolati ad ogni inserimento, diventando di fatto non validi.
Quindi nella tua lista dovresti metterci un puntatore all'elemento Value, non un iteratore della mappa.

E' quello che ho pensato anche io mentre stavo per andare in doccia...la map usa un qualche tipo di albero che, per mantenere bilanciato, rimescola i dati ad ogni inserimento!!  :cry:
Mettendo un puntatore al Value, poi potrò fornire lo stesso iteratore in output a cbegin() e cend() ?

Citazione
Altra cosa, in quel codice stai inserendo elementi vuoti dentro la lista. Se ti funzionano gli assegnamenti è solo per pura fortuna.

Funzionano...il problema è sempre nel metodo insert(), vero?
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Offline clros

  • ASM Lover
  • *****
  • Post: 457
  • Karma: +3/-1
    • Mostra profilo
Re: [C++] overload dell'operator[]
« Risposta #10 il: 26 Novembre 2011, 17:13:44 »
Citazione da: "TheKaneB"
Ora ricordo una cosa abbastanza subdola e bastarda: gli iteratori delle mappe, dal momento che si tratta di una struttura dati "ordinata", vengono rimescolati ad ogni inserimento, diventando di fatto non validi.
Quindi nella tua lista dovresti metterci un puntatore all'elemento Value, non un iteratore della mappa.

E' quello che ho pensato anche io mentre stavo per andare in doccia...la map usa un qualche tipo di albero che, per mantenere bilanciato, rimescola i dati ad ogni inserimento!!  :cry:
Mettendo un puntatore al Value, poi potrò fornire lo stesso iteratore in output a cbegin() e cend() ?

Citazione
Altra cosa, in quel codice stai inserendo elementi vuoti dentro la lista. Se ti funzionano gli assegnamenti è solo per pura fortuna.

Funzionano...il problema è sempre nel metodo insert(), vero?
« Ultima modifica: 01 Gennaio 1970, 02:00:00 da Guest »
Claudio CP La Rosa

Tags: