Write-unico riferimento in C ++?
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