Вопрос

Есть ли способ кодировать ссылку только на запись на объект? Например, предположим, что был класс 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
};

Есть ли способ гарантировать, что нельзя сделать это:

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

С другой стороны, это даже стоит того? Я знаю, что некоторые люди предполагать Эти программисты не будут сознательно Clobber системы, но тогда у нас есть частные переменные в первую очередь, а? Было бы неплохо просто сказать, что это «неопределенное поведение», но просто кажется немного неуверенным.

Редактировать: Хорошо, я понимаю идею расстройки рутины, но как это будет достигнуто?

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

foo.unlock (); Использование заданного процедуры потребуется копия для каждого записи:

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

Хотя вы можете тривезиально оптимизировать в этом конкретном случае, если, скажем, несколько разных потоков являются толкающими элементами, и, возможно, даже стирание, это может стать довольно много избыточного копирования памяти (то есть по одному на критический раздел).

Нет правильного решения

Другие советы

Да, вы можете создать класс обертки, который становится недействительным, когда разблокировка вызывается и возвращает обертку, вместо того, чтобы возвращать ссылку, и вы можете перегружать оператор присваивания, чтобы назначить ссылку. Хитрость заключается в том, что вам нужно вешать на ссылку на внутренние данные обертки, чтобы при названии разблокировки, перед выпуском блокировки вы недействители ли вы создали какие-либо обертки.

Общий способ дифференцироваться между Getbers и Benters - это Const - Ness объекта:

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

Теперь, если вы хотите получить доступ только для чтения, сделайте Mutex Const:

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

Вы можете связать объект Non-Const к ссылке Const:

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

Обратите внимание, что то lock() а также unlock() Функции по своей природе небезопасны перед лицом исключения:

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
}

Обычный способ решить это Рай. (Приобретение ресурсов является инициализация):

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

Обратите внимание, что я также переместил функции Accessor в класс блокировки, чтобы не было возможности получить доступ к разблокированным данным.

Вы можете использовать это так:

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

Вы можете инкапсулировать данные как частные и подвергать процедуру записи. В пределах этой рутины вы можете заблокировать свой Mutex, давая вам подобное поведение на то, для чего вы стреляете.

Вы можете использовать функцию участника как следующее:

void set_data(const T& var);

Вот как доступно только для записи доступа в C ++.

Нет. Нет способа гарантировать что угодно о чтении и записи памяти на небезопасных языках, таких как C ++, где вся память обрабатывается как один большой массив.


Редактировать Не уверен, почему все недостатки; Это правильно и актуально.

В безопасном языках, таких как Java или C #, вы, безусловно, можете гарантировать, что, например, правильно реализованные неизменные типы останутся неизменными. Такая гарантия никогда не может быть сделана в C ++.

Страх не столько вредоносных пользователей, так как он является случайным инвалидным указателям; Я работал на проектах C ++, где неизменные типы были мутированы из-за неверного указателя в совершенно неродственном коде, вызывая ошибки, которые являются очень сильно трудно отследить. Эта гарантия - какие только безопасные языки могут сделать - как полезны и важны.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top