Frage

Gibt es eine Möglichkeit, um Code eine Nur-Schreib-Referenz auf ein Objekt? Angenommen, gab es eine Mutex-Klasse:

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

Gibt es eine Möglichkeit zu garantieren, dass man nicht dieses tun konnte:

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

Auf der anderen Seite ist es auch wert? Ich weiß, dass einige Leute übernehmen , dass Programmierer nicht absichtlich das System clobber, aber dann, warum wir private Variablen in erster Linie haben, nicht wahr? Es wäre schön, nur zu sagen, es ist „nicht definiertes Verhalten“, aber das scheint nur ein wenig zu unsicher.

EDIT: OK, ich verstehe die Idee einer Setter Routine, aber wie dies erreicht werden würde

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

foo.unlock (); eine Reihe Routine würde für jeden Schreib eine Kopie benötigt:

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

wenn Sie in diesem speziellen Fall trivialer optimize könnte, wenn, sagen wir, verschiedene Fäden alle Schubelemente sind, und vielleicht sogar ein paar zu löschen, kann dies zu einem ziemlich viel überschüssige Speicher zu kopieren (dh eine pro kritischen Abschnitt).

Keine korrekte Lösung

Andere Tipps

Ja, können Sie eine Wrapper-Klasse erstellen, die für ungültig erklärt wird, wenn Unlock aufgerufen wird und die Hülle zurückkehren, anstatt den Bezug zurückzukehren, und Sie können den Zuweisungsoperator Überlastung auf den Referenz zuzuweisen. Der Trick ist, dass Sie an den Wrapper internen Daten auf eine Referenz hängen müssen, dass so, wenn Unlock aufgerufen wird, bevor die Sperre Lösen Sie Wrapper entkräften, dass Sie erstellt haben.

Der gemeinsame Weg zwischen Getter und Setter zu unterscheiden ist von der const-ness des Objektes:

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

Nun, wenn Sie nur lesenden Zugriff haben wollen, machen den Mutex const:

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

können Sie binden ein nicht-konstantes Objekt zu einer const-Referenz:

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

Beachten Sie, dass die lock() und unlock() Funktionen sind von Natur aus unsicher angesichts der Ausnahmen:

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
}

Der üblicher Weg, dies zu lösen, ist RAH (Ressourcenerfassung ist Initialisierung):

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

Beachten Sie, dass ich auch die Zugriffsfunktionen auf die Schlossklasse verschoben haben, so dass es keine Möglichkeit gibt, den Zugang freigeschaltet Daten.

Sie können damit wie folgt aus:

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

Sie könnten die Daten als privat einkapseln und eine Schreibroutine aus. Innerhalb dieser Routine können Sie Ihre Mutex sperren, so dass Sie ein ähnliches Verhalten zu dem, was Sie schießen.

Sie können eine Elementfunktion wie die folgenden verwenden:

void set_data(const T& var);

Dies ist, wie Schreibzugriff ist in C ++ angewandt.

Nein. Es gibt keine Möglichkeit zu garantieren, etwas über das Lesen und in unsicheren Sprachen wie C ++ Speicher zu schreiben, in dem alle Speicher wie eine große Array behandelt wird.


[Bearbeiten] Nicht sicher, warum alle downvotes; das ist richtig und relevant sind.

In sicheren Sprachen wie Java oder C #, können Sie sicher, dass garantieren, zum Beispiel, werden richtig implementierter unveränderliche Arten unveränderlich bleiben. Eine solche Garantie kann nie in C ++ gemacht werden.

Die Angst ist nicht so sehr böswillige Benutzer, wie es zufälliger invalid-Zeiger ist; Ich habe auf C ++ Projekten gearbeitet, wo unveränderliche Typen haben in keinem Zusammenhang Code aufgrund eines ungültigen Zeiger mutiert worden, Fehler zu verursachen, die sind extrem schwer aufzuspüren. Diese Garantie - die einzige sichere Sprachen machen können -. Ist nützlich und wichtig

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top