مرجع الكتابة فقط في C ++؟
سؤال
هل هناك طريقة لترميز إشارة الكتابة فقط إلى كائن؟ على سبيل المثال ، افترض أن هناك فئة 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 ++ حيث تم تحوير أنواع غير قابلة للتغيير بسبب مؤشر غير صالح في رمز غير مرتبط تمامًا ، مما تسبب في الأخطاء التي هي الى ابعد حد من الصعب تعقب. هذا الضمان - أي اللغات الآمنة فقط التي يمكن أن تصنعها - مفيد ومهم.