Pregunta

¿Hay una manera de programar una referencia de sólo escritura a un objeto? Por ejemplo, supongamos que había una clase 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
};

¿Hay una manera de garantizar que no se podía hacer esto:

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

Por otro lado, ¿es siquiera vale la pena? Sé que algunas personas asumen que los programadores no deliberadamente clobber el sistema, pero entonces, ¿por qué tenemos variables privadas en primer lugar, ¿eh? Sería bueno que acaba de decir que es "un comportamiento indefinido", pero que apenas se parece un poco demasiado inseguro.

EDIT: OK, entiendo la idea de una rutina organismo, pero ¿cómo podría esto ser realizado

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

foo.unlock (); Utilizando una rutina fija requeriría una copia para cada escritura:

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);
}

aunque se puede trivialmente a optimizar en este caso concreto, si, por ejemplo, varios hilos diferentes están empujando a los elementos, y tal vez incluso borrando algunos, esto puede convertirse en un poco de exceso de memoria de copia (es decir, uno por cada sección crítica).

No hay solución correcta

Otros consejos

Sí, se puede crear una clase contenedora que queda invalidada cuando se llama de desbloqueo y devolver el envoltorio, en lugar de devolver la referencia, y se puede sobrecargar su operador de asignación para asignar a la referencia. El truco es que se necesita para colgar en una referencia a los datos internos de la envoltura, de manera que cuando se llama de desbloqueo, antes de liberar el bloqueo, se invalidan cualquier envoltorios que ha creado.

La forma común para diferenciar entre captadores y definidores es por el const-dad del objeto:

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;
};

Ahora, si usted quiere tener acceso de sólo lectura, hacen que el mutex const:

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

Se puede enlazar un objeto no constante a una referencia constante:

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

Tenga en cuenta que las funciones lock() y unlock() son intrínsecamente inseguros en la cara de excepciones:

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
}

La forma habitual de resolver esto es RAII (Raii):

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_;
};

Tenga en cuenta que también he movido las funciones de acceso a la clase de bloqueo, por lo que no hay manera de acceder a los datos desbloqueado.

Puede utilizar este como esto:

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
  }
}

Se puede encapsular los datos como privado y exponer una rutina de escritura. Dentro de esa rutina que podría bloquear el mutex, que le da un comportamiento similar a lo que está disparando a.

Se puede utilizar una función miembro como el siguiente:

void set_data(const T& var);

Se trata de cómo se aplica el acceso de sólo escritura en C ++.

No. No hay ninguna manera de garantizar nada sobre la lectura y la escritura en la memoria idiomas inseguras como C ++, donde toda la memoria es tratada como una gran matriz.


[Editar] No sé por qué todos los downvotes; esto es correcto y relevante.

En los lenguajes de seguros, como Java o C #, que sin duda puede garantizar que, por ejemplo, tipos inmutables implementados adecuadamente, permanecerán inmutables. Esta garantía no se puede hacer en C ++.

El miedo no es tanto usuarios maliciosos, ya que es accidentales no válidos triples; He trabajado en proyectos de C ++, donde han sido mutados tipos inmutables, debido a un puntero no válido en el código completamente ajenos, provocando errores que son muy difíciles de localizar. Esta garantía - lo que sólo pueden hacer seguras idiomas -. Es útil e importante

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top