Sovraccarico dell’operatore di indicizzazione di indicizzazione C ++ in modo da consentire risposte agli aggiornamenti

Considerare il compito di scrivere una class indicizzabile che sincronizza automaticamente il suo stato con un archivio dati esterno (ad esempio un file). Per fare ciò, la class dovrebbe essere resa consapevole delle modifiche al valore indicizzato che potrebbero verificarsi. Sfortunatamente il solito approccio all’operatore di overload [] non lo consente, ad esempio …

Type& operator[](int index) { assert(index >=0 && index < size); return state[index]; } 

C’è un modo per distinguere tra un valore a cui si accede e un valore che viene modificato?

 Type a = myIndexable[2]; //Access myIndexable[3] = a; //Modification 

Entrambi questi casi si verificano dopo che la funzione è tornata. C’è qualche altro approccio all’operatore di overload [] che potrebbe avere più senso?

Dall’operatore [] puoi solo dire veramente l’accesso.
Anche se l’ quadro esterna utilizza la versione non a costo, ciò non significa che si verificherà una scrittura piuttosto che potrebbe aver luogo.

In quanto tale, ciò che devi fare è restituire un object in grado di rilevare le modifiche.
Il modo migliore per farlo è racchiudere l’object con una class che sovrascrive l’ operator= . Questo wrapper può quindi informare l’archivio quando l’object è stato aggiornato. Dovresti anche eseguire l’override operator Type (cast) in modo che sia ansible recuperare una versione const dell’object per gli accessi in lettura.

Quindi potremmo fare qualcosa del genere:

 class WriteCheck; class Store { public: Type const& operator[](int index) const { return state[index]; } WriteCheck operator[](int index); void stateUpdate(int index) { // Called when a particular index has been updated. } // Stuff }; class WriteCheck { Store& store; Type& object; int index; public: WriteCheck(Store& s, Type& o, int i): store(s), object(o), index(i) {} // When assignment is done assign // Then inform the store. WriteCheck& operator=(Type const& rhs) { object = rhs; store.stateUpdate(index); } // Still allow the base object to be read // From within this wrapper. operator Type const&() { return object; } }; WriteCheck Store::operator[](int index) { return WriteCheck(*this, state[index], index); } 

Un’alternativa più semplice è:
Anziché fornire all’operatore [], si fornisce un metodo set specifico sull’object del negozio e si fornisce solo l’accesso in lettura tramite l’operatore []

È ansible avere (operatore non const) [] restituire un object proxy che mantiene un riferimento o un puntatore al contenitore e in cui operator = segnala il contenitore dell’aggiornamento.

(L’idea di utilizzare l’operatore const vs non-const [] è un’aringa rossa … potresti sapere che hai appena dato l’accesso non-const all’object, ma non sai se quell’accesso è ancora in corso usato per una lettura o una scrittura, quando quella scrittura è completa, o ha qualche meccanismo per aggiornare il contenitore da quel momento in poi.)

Un’altra soluzione elegante (IMHO) … In realtà si basa sul fatto che il sovraccarico const viene chiamato solo quando viene utilizzato su un object const. Innanzitutto creiamo due [] sovraccarichi, in quanto è necessario, ma utilizzando posizioni diverse:

 Type& operator[](int index) { assert(index >=0 && index < size); return stateWrite[index]; } const Type& operator[](int index) const { assert(index >=0 && index < size); return stateRead[index]; } 

Ora dovresti creare un riferimento all'ombra del tuo object quando devi "leggerlo" come segue:

 const Indexable& myIndexableRead = myIndexable; // create the shadow Type a = myIndexableRead[2]; //Access myIndexable[3] = a; //Modification 

La creazione di questa dichiarazione shadow in realtà non crea nulla nella memoria. Crea semplicemente un altro nome per il tuo object con accesso "const". È tutto risolto in fase di compilazione (compreso l'uso del sovraccarico const) e non influisce su nulla in fase di esecuzione: né memoria né prestazioni.

E la linea di fondo - è molto più elegante (IMHO) rispetto alla creazione di proxy di assegnazione, ecc. Devo dichiarare che l'affermazione " Dall'operatore [] si può solo dire l'accesso " è errata. Secondo lo standard C ++, la restituzione di oggetti allocati dynamicmente o di variabili globali per riferimento è il modo migliore per consentire la sua modifica diretta, incluso il caso di sovraccarico [].

Il seguente codice è stato testato:

 #include  using namespace std; class SafeIntArray { int* numbers; int size; static const int externalValue = 50; public: SafeIntArray( unsigned int size = 20 ) { this->size = size; numbers = new int[size]; } ~SafeIntArray() { delete[] numbers; } const int& operator[]( const unsigned int i ) const { if ( i < size ) return numbers[i]; else return externalValue; } int& operator[]( const unsigned int i ) { if ( i < size ) return numbers[i]; else return *numbers; } unsigned int getSize() { return size; } }; int main() { SafeIntArray arr; const SafeIntArray& arr_0 = arr; int size = arr.getSize(); for ( int i = 0; i <= size ; i++ ) arr[i] = i; for ( int i = 0; i <= size ; i++ ) { cout << arr_0[i] << ' '; } cout << endl; return 0; } 

E i risultati sono:

20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 50

Restituisce un object proxy che avrà:

  • operator = (Type const e) overloaded per le scritture
  • operatore Type () per letture

nell’esempio di accesso che dai puoi ottenere una distinzione usando una versione const:

 const Type& operator [] ( int index ) const; 

in un sidenote, l’uso di size_t come indice elimina la necessità di controllare se index> = 0

  #include "stdafx.h" #include  template class MyVector { T* _Elem; // a pointer to the elements int _Size; // the size public: // constructor MyVector(int _size):_Size(_size), _Elem(new T[_size]) { // Initialize the elemets for( int i=0; i< _size; ++i ) _Elem[i] = 0.0; } // destructor to cleanup the mess ~MyVector(){ delete []_Elem; } public: // the size of MyVector int Size() const { return _Size; } // overload subscript operator T& operator[]( int i ) { return _Elem[i]; } }; int _tmain(int argc, _TCHAR* argv[]) { MyVector vec(10); vec[0] =10; vec[1] =20; vec[2] =30; vec[3] =40; vec[4] =50; std::cout<<"Print vector Element "<operator = e funzioni che non sono ereditate in C ++?
  • Costruttore di sovraccarico per Case Class di Scala?
  • Main () è sovraccarico in C ++?
  • SFINAE funziona in modo diverso nei casi di parametri modello tipo e non tipo
  • Funzione statica di sovraccarico C ++ con funzione non statica
  • Perché l'operatore incaricato deve restituire un riferimento all'object?
  • Gestione del sovraccarico di std :: endl?
  • Sovraccarico con diverso tipo di reso in Java?