سؤال

أثناء تصحيح الأخطاء في تطبيق متعدد مؤشرات الترابط، أخيرا في المشكلة في هذا البيان:

CSingleLock(&m_criticalSection, TRUE);

لاحظ أنه يقوم بإنشاء كائن لم يذكر اسمه من فئة CSingLelock وبالتالي يتم إلغاء تأمين كائن القسم الحرج مباشرة بعد هذا البيان. من الواضح أن هذا ليس ما يريده الهامس. سبب هذا الخطأ بسبب خطأ كتابة بسيط. سؤالي هو، هل هناك ما يمكنني منع الكائن المؤقت للصف الذي يتم إنشاؤه في وقت الترجمة نفسه نفسه، أي نوع من التعليمات البرمجية أعلاه يجب أن يولد خطأ مترجم. بشكل عام، أعتقد كلما أن الفصل يحاول إجراء نوع من الاستحواذ على الموارد، ثم لا ينبغي السماح بالكائن المؤقت لتلك الفئة. هل هناك أي طريقة لفرضها؟

هل كانت مفيدة؟

المحلول

يحرر: كما ملاحظات J_random_hacker، من الممكن إجبار المستخدم على إعلان كائن مسمى من أجل إخراج قفل.

ومع ذلك، حتى إذا كان إنشاء المؤخرات محظورة بطريقة أو بأخرى لفصلك، فيمكن للمستخدم طرح خطأ مماثل:

// take out a lock:
if (m_multiThreaded)
{
    CSingleLock c(&m_criticalSection, TRUE);
}

// do other stuff, assuming lock is held

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

خطأ آخر محتمل:

 CSingleLock *c = new CSingleLock(&m_criticalSection, TRUE);

 // do other stuff, don't call delete on c...

التي من شأنها أن تقودك إلى طرح "هل هناك أي طريقة يمكنني إيقاف المستخدم من صفي من تخصيصها على كومة"؟ التي تكون الإجابة هي نفسها.

في C ++ 0x، ستكون هناك طريقة أخرى للقيام بكل هذا، باستخدام Lambdas. تحديد وظيفة:

template <class TLock, class TLockedOperation>
void WithLock(TLock *lock, const TLockedOperation &op)
{
    CSingleLock c(lock, TRUE);
    op();
}

تلتقط هذه الوظيفة الاستخدام الصحيح ل Csinglelock. الآن دع للمستخدمين يقومون بذلك:

WithLock(&m_criticalSection, 
[&] {
        // do stuff, lock is held in this context.
    });

هذا أصعب بكثير للمستخدم البرغي. يبدو بناء الجملة غريبا في البداية، ولكن [&] متبوعا بجدوق التعليمات البرمجية يعني "تحديد الوظيفة التي لا تتطلب أي args، وإذا كنت تشير إلى أي شيء بالاسم وتكون اسم شيء خارج (مثل متغير محلي في المحتوي عليه وظيفة) اسمحوا لي بالوصول إليها عن طريق مرجع غير الاتصال، حتى أتمكن من تعديلها.)

نصائح أخرى

أولا، earwicker يجعل بعض النقاط الجيدة - لا يمكنك منع كل إساءة استخدام عرضي لهذا البناء.

ولكن بالنسبة لحالتك المحددة، يمكن تجنب ذلك في الواقع. ذلك لأن C ++ يقوم بتمييز واحد (غريب) فيما يتعلق بالأشياء المؤقتة: لا يمكن أن تأخذ الوظائف المجانية مراجع غير مؤقتة إلى كائنات مؤقتة. لذلك، من أجل تجنب الأقفال التي تخفف داخل وخارج الوجود، ما عليك سوى نقل رمز القفل خارج CSingleLock منشئ وإلى وظيفة مجانية (يمكنك أن تصنع صديقا لتجنب تعريض Internals كطرق):

class CSingleLock {
    friend void Lock(CSingleLock& lock) {
        // Perform the actual locking here.
    }
};

لا يزال فتح فتح في المدمر.

ليستخدم:

CSingleLock myLock(&m_criticalSection, TRUE);
Lock(myLock);

نعم، إنه غير عملي قليلا للكتابة. ولكن الآن، سوف يشكو المحول البرمجي إذا حاولت:

Lock(CSingleLock(&m_criticalSection, TRUE));   // Error! Caught at compile time.

لأن المعلمة المرجع غير القست Lock() لا يمكن ربطها مؤقتا.

ربما بشكل مدهش، طرق الطبقة يمكن تعمل على المؤخرات - لهذا السبب Lock() يحتاج إلى أن تكون وظيفة مجانية. إذا كنت تسقط friend المواصفات ومعلمة الوظيفة في أعلى مقتطف لجعل Lock() طريقة، ثم يسمح لك المترجم بسعادة بالكتابة:

CSingleLock(&m_criticalSection, TRUE).Lock();  // Yikes!

MS Compiler ملاحظة: إصدارات MSVC ++ تصل إلى Visual Studio .NET 2003 المسموح بها بشكل غير صحيح الوظائف لربط مراجع غير الاتصالات في الإصدارات قبل VC ++ 2005. تم إصلاح هذا السلوك في VC ++ 2005 وما فوق.

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

لا أعتقد ذلك.

في حين أنه ليس شيئا معقولا للقيام به - كما اكتشفت مع علة الخاص بك - لا يوجد شيء "غير قانوني" حول البيان. ليس لدى المحول البرمجي أي طريقة لمعرفة ما إذا كانت قيمة الإرجاع من الطريقة "حيوية" أم لا.

مترجم لا ينبغي أن لا يسمح بإنشاء كائن مؤقت، IMHO.

حالات خاصة مثل تقلص ناقل تحتاج حقا إلى كائن مؤقت ليتم إنشاؤه.

std::vector<T>(v).swap(v);

على الرغم من أنها صعبة بعض الشيء ولكن لا تزال مراجعة التعليمات البرمجية واختبار الوحدة يجب أن تصطاد هذه القضايا.

خلاف ذلك، هنا هو حل رجل فقير:

CSingleLock aLock(&m_criticalSection); //Don't use the second parameter whose default is FALSE

aLock.Lock();  //an explicit lock should take care of your problem

ماذا عن ما يلي؟ انتهاكا قليلا من قبل Preprocessor، لكنه ذكي بما فيه الكفاية، أعتقد أنه ينبغي تضمينه:

class CSingleLock
{
    ...
};
#define CSingleLock class CSingleLock

الآن نسيان تسمية النتائج المؤقتة في خطأ، لأنه في حين أن ما يلي صالح C ++:

class CSingleLock lock(&m_criticalSection, true); // Compiles just fine!

نفس الرمز، ولكن حذف الاسم، ليس:

class CSingleLock(&m_criticalSection, true); // <-- ERROR!

أرى أنه في غضون 5 سنوات لم يأت أحد مع الحل الأكثر بسيطة:

#define LOCK(x) CSingleLock lock(&x, TRUE);
...
void f() {
   LOCK(m_criticalSection);

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top