Question

Yat-il un moyen de coder une référence en écriture seule à un objet? Par exemple, supposons que il y avait une 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
};

Yat-il un moyen de garantir que l'on ne pouvait pas faire ceci:

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

Par contre, est-il encore la peine? Je sais que certaines personnes présument que les programmeurs ne seront pas délibérément clobber le système, mais, pourquoi nous avons des variables privées en premier lieu, hein? Ce serait bien de dire que c'est « un comportement non défini », mais que tout semble un peu trop peu sûr.

EDIT: OK, je comprends l'idée d'une routine setter, mais comment cela serait accompli

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

; En utilisant une routine nécessiterait une copie pour chaque écriture:

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

si vous pouvez trivialement optimize dans ce cas spécifique, si, par exemple, plusieurs threads différents sont autant d'éléments poussent, et peut-être même d'effacer quelques-unes, cela peut devenir un peu d'excès la copie mémoire (soit un par section critique).

Pas de solution correcte

Autres conseils

Oui, vous pouvez créer une classe wrapper qui devient invalidée lorsque déverrouillage est appelé et retourner l'emballage, au lieu de retourner la référence, et vous pouvez surcharger son opérateur d'affectation à attribuer à la référence. L'astuce est que vous devez accrocher une référence aux données internes de l'emballage, de sorte que lorsque Unlock, avant de libérer le verrou, vous annuler toute wrappers que vous avez créé.

La voie commune de différencier les accesseurs est par la const-ness de l'objet:

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

Maintenant, si vous voulez avoir accès en lecture seule, faire le mutex const:

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

Vous pouvez lier un objet non-const à une référence const:

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

Notez que les fonctions de lock() et unlock() sont intrinsèquement dangereux face à des exceptions:

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 façon habituelle de résoudre ce problème est RAII (acquisition de ressources est l'initialisation):

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

Notez que j'ai aussi déplacé les fonctions accesseurs à la classe de verrouillage, de sorte qu'il n'y a aucun moyen de données d'accès déverrouillé.

Vous pouvez utiliser cela comme ceci:

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

Vous pouvez résumer les données en tant que privé et d'exposer une routine d'écriture. Au sein de cette routine, vous pouvez verrouiller votre mutex, vous donnant un comportement similaire à ce que vous filmez pour.

Vous pouvez utiliser une fonction membre comme suit:

void set_data(const T& var);

Voici comment l'accès en écriture seule est appliquée en C ++.

Non

. Il n'y a aucun moyen de quoi que ce soit de garantie sur la lecture et l'écriture dans la mémoire des langues dangereuses comme C ++, où toute la mémoire est traitée comme un grand tableau.


[Modifier] Je ne sais pas pourquoi tous les downvotes; cela est correct et pertinent.

Dans les langues de sécurité, telles que Java ou C #, vous pouvez certainement garantir que, par exemple, les types immuables correctement mis en œuvre, resteront immuables. Une telle garantie ne peut jamais être en C ++.

La peur est pas si les utilisateurs malveillants beaucoup plus car il est invalide pointeurs accidentels; Je travaille sur les projets de C où les types immuables ont été mutées en raison d'un pointeur non valide dans le code complètement différent, ce qui provoque des bugs qui sont très difficile à traquer. Cette garantie - qui seules langues de sécurité peuvent faire -. Est à la fois utile et important

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top