Domanda

C'è un modo di codificare un riferimento di sola scrittura a un oggetto? Ad esempio, supponiamo che c'era una classe mutex:

template <class T> class mutex {
protected:
   T _data;
public:
   mutex();
   void lock(); //locks the mutex
   void unlock(); //unlocks the mutex
   T& data(); //returns a reference to the data, or throws an exception if lock is unowned
};

C'è un modo per garantire che non si poteva fare questo:

mutex<type> foo;
type& ref;
foo.lock();
foo.data().do_stuff();
ref = foo.data();
foo.unlock();
//I have a unguarded reference to foo now

D'altra parte, è ancora vale la pena? So che alcune persone presuppongono che i programmatori non sarà deliberatamente clobber il sistema, ma poi, perché dobbiamo variabili private, in primo luogo, eh? Sarebbe bello per solo dire che è "un comportamento indefinito", ma che sembra solo un po 'troppo insicuro.

EDIT: OK, ho capito l'idea di una routine di setter, ma come sarebbe questo essere realizzato

?
mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
   foo.data().push_back(i);
}

foo.unlock (); Utilizzando un set di routine richiederebbe una copia per ogni processo di scrittura:

mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
   vector<int> copy = foo.read();
   copy.push_back(i);
   foo.write(copy);
}

anche se si potrebbe banalmente ottimizzare in questo caso specifico, se, ad esempio, diversi thread diversi sono tutti elementi spingendo, e forse anche la cancellazione di alcuni, questo può diventare un po 'di memoria in eccesso copiare (cioè uno per ogni sezione critica).

Nessuna soluzione corretta

Altri suggerimenti

Sì, è possibile creare una classe wrapper che diventa invalidato quando sblocco è chiamata e tornare l'involucro, invece di restituire il riferimento e si può sovraccaricare il suo operatore di assegnazione per assegnare al riferimento. Il trucco è che avete bisogno di appendere su un riferimento ai dati interni del involucro, in modo che quando sblocco è chiamato, prima di rilasciare il blocco, si decadere qualsiasi wrapper che si è creato.

Il modo più comune per distinguere tra getter e setter è dal const-ness dell'oggetto:

template <class T> class mutex {
public:
   mutex();
   void lock();
   void unlock();
         T& data();       // cannot be invoked for const objects
   const T& data() const; // can be invoked for const objects
protected:
   T _data;
};

Ora, se si vuole avere accesso in sola lettura, rendono il const mutex:

void read_data(const mutex< std::vector<int> >& data)
{
   // only const member functions can be called here
}

È possibile associare un oggetto non const a un riferimento const:

// ...
mutex< std::vector<int> > data;
data.lock();
read_data(data);
data.unlock();
// ...

Si noti che le funzioni lock() e unlock() sono intrinsecamente insicuri di fronte alle eccezioni:

void f(const mutex< std::vector<int> >& data)
{
  data.lock();
  data.data().push_back(42); // might throw exception
  data.unlock(); // will never be reached in push_back() throws
}

Il solito modo per risolvere questo è RAII (acquisizione delle risorse è di inizializzazione):

template <class T> class lock;

template <class T> class mutex {
public:
   mutex();
protected:
   T _data;
private:
   friend class lock<T>;
   T& data();
   void lock();
   void unlock();
};

template <class T> class lock {
public:
  template <class T> {
  lock(mutex<T>& m) m_(m) {m_.lock();}
  ~lock()                 {m_.unlock();}

         T& data()        {return m_.data();}
   const T& data() const  {return m_.data()}
private:
  mutex<T>& m_;
};

Si noti che ho anche spostato le funzioni di accesso alla classe di blocco, in modo che non v'è alcun modo per accedere sbloccato dati.

È possibile utilizzare questo in questo modo:

void f(const mutex< std::vector<int> >& data)
{
  {
    lock< std::vector<int> > lock_1(data);
    std::cout << lock1.data()[0]; // fine, too
    lock1.data().push_back(42);   // fine
  }
  {
    const lock< std::vector<int> > lock_2(data); // note the const
    std::cout << lock1.data()[0];  // fine, too
    // lock1.data().push_back(42); // compiler error
  }
}

Si potrebbe incapsulare i dati come privato ed esporre una routine di scrittura. All'interno di tale procedura si potrebbe bloccare il mutex, dando un comportamento simile a quello che si sono riprese per.

È possibile utilizzare una funzione di membro come il seguente:

void set_data(const T& var);

In questo modo l'accesso in sola scrittura è applicata in C ++.

No. Non v'è alcun modo per garantire nulla di lettura e scrittura della memoria in linguaggi non sicuri come il C ++, in cui tutta la memoria è trattato come una grande serie.


[Edit] Non capisco perché tutte le downvotes; questo è corretto e pertinente.

In linguaggi sicuri, come Java o C #, si può certamente garantire che, per esempio, i tipi immutabili correttamente implementati rimarrà immutabile. Tale garanzia non può mai essere fatto in C ++.

La paura non è utenti tanto dannosi quanto è accidentale non validi-puntatori; Ho lavorato su progetti di C ++ in cui sono stati mutati i tipi immutabili a causa di un puntatore non valido nel codice completamente indipendenti, causando bug che sono estremamente difficili da rintracciare. Questa garanzia - che solo le lingue sicuri possono fare -. È sia utile e importante

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top