سؤال

هل هناك طريقة لترميز إشارة الكتابة فقط إلى كائن؟ على سبيل المثال ، افترض أن هناك فئة 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

من ناحية أخرى ، هل يستحق ذلك؟ أعلم أن بعض الناس يفترض أن المبرمجين لن يربطوا النظام عن عمد ، ولكن بعد ذلك ، لماذا لدينا متغيرات خاصة في المقام الأول ، إيه؟ سيكون من الجيد أن نقول إنه "سلوك غير محدد" ، لكن هذا يبدو غير آمن قليلاً.

تحرير: حسنًا ، أفهم فكرة روتين Setter ، لكن كيف سيتم تحقيق ذلك؟

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

على الرغم من أنه يمكنك تحسين هذه الحالة المحددة ، على سبيل المثال ، على سبيل المثال ، العديد من الخيوط المختلفة التي تدفع جميعها عناصر ، وربما حتى محو قليل ، يمكن أن يصبح هذا جزءًا كبيرًا من نسخ الذاكرة الزائدة (أي واحدة لكل قسم حرج).

لا يوجد حل صحيح

نصائح أخرى

نعم ، يمكنك إنشاء فئة Wrapper التي يتم إبطالها عند استدعاء إلغاء القفل وإرجاع Wrapper ، بدلاً من إرجاع المرجع ، ويمكنك زيادة تحميل عامل المهمة الخاص به لتعيين المرجع. الحيلة هي أنك تحتاج إلى التمسك بالإشارة إلى البيانات الداخلية للركب ، بحيث يتم استدعاء فتحها ، قبل إطلاق القفل ، تقوم بإبطال أي أغلفة قمت بإنشائها.

الطريقة الشائعة للتمييز بين getters والمستقلين هي عن طريق كونها الكائن:

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

الآن ، إذا كنت ترغب في الوصول إلى القراءة فقط ، فقم بإجراء موتيكس كونست:

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

يمكنك ربط كائن غير مؤشر إلى مرجع 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_;
};

لاحظ أنني قمت أيضًا بنقل وظائف الملحق إلى فئة القفل ، بحيث لا توجد وسيلة للوصول إلى البيانات غير المؤمنة.

يمكنك استخدام هذا مثل هذا:

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