سؤال

كنت أستخدم لغة Java لعدد من السنوات، لذا لم أتتبع لغة C++.لديه أخيراً تمت إضافة جملة إلى معالجة استثناءات C++ في تعريف اللغة؟

هل هناك لغة مفضلة تحاكي تجربة Java/أخيرًا؟

أنا منزعج أيضًا من أن لغة C++ لا تحتوي على نوع فائق نهائي لجميع الاستثناءات المحتملة التي يمكن طرحها - مثل فئة Java's Throwable.

أستطيع أن أكتب:

try {
  // do something
} catch(...) {
  // alas, can't examine the exception
  // can only do cleanup code and perhaps rethrow, ala:
  throw;
}

تعديل الإضافة:

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

تعديل ملحق جديد:

حسنًا، بدلاً من أخيراً ثم ميزة الإغلاق ربما؟سيكون الإغلاق المقترن بنهج ScopeGuard (انظر إحدى الإجابات أدناه) وسيلة لإنجاز التنظيف مع الإجراءات التعسفية والوصول إلى سياق النطاق الخارجي لرمز التنظيف.يمكن إجراء التنظيف بالطريقة التي تظهر في برمجة روبي حيث توفر كتل التنظيف عند فتح المورد.أليست ميزة الإغلاق التي يتم النظر فيها لـ C ++؟

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

المحلول

من خلال الاستخدام الفعال للمدمرات.عندما يتم طرح استثناء في كتلة محاولة، سيتم تدمير أي كائن تم إنشاؤه داخلها على الفور (وبالتالي يتم استدعاء أداة التدمير الخاصة به).

وهذا يختلف عن Java حيث ليس لديك أي فكرة عن متى سيتم استدعاء أداة الإنهاء للكائن.

تحديث:مباشرة من فم الحصان: لماذا لا يوفر C++ بنية "أخيرة"؟

نصائح أخرى

بلدي $.02.لقد قمت بالبرمجة بلغات مُدارة مثل C# وJava لسنوات، ولكنني اضطررت إلى التبديل إلى C++ لأغراض السرعة.في البداية لم أستطع أن أصدق كيف اضطررت إلى كتابة توقيع الطريقة مرتين في ملف الرأس ثم ملف CPP، ولم يعجبني عدم وجود حظر أخيرًا، وعدم وجود مجموعة من البيانات المهملة تعني تتبع تسرب الذاكرة في كل مكان - يا إلهي لم يعجبني ذلك على الإطلاق!

ومع ذلك، كما قلت، اضطررت إلى استخدام C++.لذلك اضطررت إلى تعلمها بجدية، والآن فهمت أخيرًا جميع مصطلحات البرمجة مثل RAII وحصلت على كل التفاصيل الدقيقة للغة وما إلى ذلك.لقد استغرق الأمر بعض الوقت ولكنني الآن أرى مدى اختلاف اللغة مقارنة بلغة C# أو Java.

أعتقد هذه الأيام أن لغة C++ هي أفضل لغة موجودة!نعم، أستطيع أن أفهم أن هناك ما أسميه "القشرة" أحيانًا (أشياء تبدو غير ضرورية للكتابة)، ولكن بعد استخدام اللغة بجدية، غيرت رأيي بشأنها تمامًا.

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

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

مع احترامي، أعتقد أن ما يحدث هو أنك معتاد على استخدام Java الآن، تمامًا كما كنت.

إجابة C++ هي RAII:سيتم تنفيذ أداة تدمير الكائن عندما يخرج عن النطاق.سواء بالعودة أو بالاستثناء أو بأي شيء كان.إذا قمت بمعالجة الاستثناء في مكان آخر، فيمكنك التأكد من أن جميع الكائنات بدءًا من الوظيفة التي تم استدعاؤها وصولاً إلى المعالج الخاص بك سيتم تدميرها بشكل صحيح من خلال استدعاء أداة التدمير الخاصة بها.سوف ينظفون لك.

يقرأ http://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

لم تتم إضافة أي شيء أخيرًا إلى C++، ومن غير المرجح أن تتم إضافته على الإطلاق.

الطريقة التي يستخدم بها C++ المُنشئ/المدمر تجعل الحاجة إلى ذلك غير ضرورية في النهاية.
إذا كنت تستخدم الصيد (...) للتنظيف فأنت لا تستخدم C++ بشكل صحيح.يجب أن يكون رمز التنظيف موجودًا في أداة التدمير.

على الرغم من أنه ليس من الضروري استخدامه، إلا أن لغة C++ بها استثناء std::exception.
إن إجبار المطورين على الاشتقاق من فئة معينة لاستخدام الاستثناء يتعارض مع فلسفة C++ البسيطة.وهذا أيضًا هو سبب عدم مطالبتنا باشتقاق جميع الفئات من Object.

يقرأ: هل يدعم C++ الكتل "أخيرًا"؟(وما هو "RAII" الذي أسمع عنه دائمًا؟)

يعد استخدام أخيرًا أكثر عرضة للخطأ من استخدام المدمرات للقيام بالتنظيف.
هذا لأنك تجبر مستخدم الكائن على القيام بالتنظيف بدلاً من المصمم/المنفذ للفئة.

حسنًا ، لا بد لي من إضافة إجابة للنقاط التي ذكرتها في مشاركة إجابة منفصلة:(سيكون الأمر أكثر ملاءمة إذا قمت بتحرير هذا في السؤال الأصلي، بحيث لا ينتهي به الأمر في الأسفل أقل الإجابات عليه.

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

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

سيتم التنظيف في كتلة أخيرًا ما إذا كان هناك استثناء تم طرحه أم لا - وهو ما يريد المرء دائمًا أن يحدث عند وجود رمز التنظيف.

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

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

لقد ابتليت مطورو C ++ من اليوم الأول بالاضطرار إلى تكرار إجراءات التنظيف التي تظهر في كتل الصيد في تدفق الكود الذي يحدث عند الخروج الناجح من كتلة المحاولة.المبرمجين Java و C# فقط يفعلون ذلك مرة واحدة في الكتلة الأخيرة.

لا.لم يعاني مبرمجو C++ من ذلك أبدًا.المبرمجين C لديهم.ومبرمجو لغة C الذين أدركوا أن لغة ++C لديها فصول دراسية، ثم أطلقوا على أنفسهم اسم مبرمجي لغة C++.

أنا أبرمج بلغة C++ وC# يوميًا، وأشعر أنني مبتلى بإصرار C# السخيف على ضرورة توفير جملة أخيرة (أو عبارة using block) في كل مرة أستخدم اتصال قاعدة البيانات أو أي شيء آخر يجب تنظيفه.

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

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

  • ابحث عن مكتبة C++ مناسبة توفر أداة التدمير هذه (تلميح:يعزز)
  • استخدم Boost::shared_ptr لتغليفه، وتزويده بعامل مخصص في وقت التشغيل، مع تحديد عملية التنظيف المطلوب القيام بها.

عندما تكتب برنامج خادم التطبيق مثل Java EE App Servers Glassfish و JBOSS ، وما إلى ذلك ، فأنت تريد أن تكون قادرًا على التقاط معلومات الاستثناء وتسجيلها - بدلاً من السماح لها بسقوط على الأرض.أو أسوأ في وقت التشغيل ويتسبب في خروج مفاجئ خادم التطبيق.لهذا السبب من المرغوب فيه للغاية أن يكون لديك فئة قاعدة شاملة لأي استثناء محتمل.وC++ لديه مثل هذه الفئة.الأمراض المنقولة جنسيا::استثناء.

فعلت C ++ منذ أيام Cfront و Java/C# معظم هذا العقد.من الواضح أن نرى أن هناك فجوة ثقافية هائلة في كيفية التعامل مع الأشياء المتشابهة بشكل أساسي.

لا، لم يسبق لك استخدام لغة C++.لقد انتهيت من استخدام CFront أو C مع الفصول الدراسية.ليس سي++.هناك فرق كبير.توقف عن وصف الإجابات بأنها ضعيفة، وقد تتعلم شيئًا عن اللغة التي كنت تعتقد أنك تعرفها.;)

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

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

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

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

هناك الكثير من المشاكل في لغة C++، لكن هذه ليست إحداها.هناك طرق تجعل Java أفضل من C++، لكن هذه ليست واحدة منها.

ستكون Java أفضل حالًا بكثير من خلال طريقة تنفيذ RAII بدلاً من المحاولة...أخيرًا.

لتجنب الاضطرار إلى تحديد فئة مجمعة لكل مورد قابل للنشر، قد تكون مهتمًا بـ ScopeGuard (http://www.ddj.com/cpp/184403758) والذي يسمح للمرء بإنشاء "منظفات" بسرعة.

على سبيل المثال:

FILE* fp = SomeExternalFunction();
// Will automatically call fclose(fp) when going out of scope
ScopeGuard file_guard = MakeGuard(fclose, fp);

مثال على مدى صعوبة الاستخدام أخيرًا بشكل صحيح.

فتح وإغلاق ملفين.
حيث تريد التأكد من إغلاق الملف بشكل صحيح.
لا يعد انتظار GC خيارًا حيث قد يتم إعادة استخدام الملفات.

في لغة سي++

void foo()
{
    std::ifstream    data("plop");
    std::ofstream    output("plep");

    // DO STUFF
    // Files closed auto-magically
}

في لغة لا تحتوي على أدوات تدمير ولكن تحتوي على جملة نهائية.

void foo()
{
    File            data("plop");
    File            output("plep");

    try
    {
        // DO STUFF
    }
    finally
    {
        // Must guarantee that both files are closed.
        try {data.close();}  catch(Throwable e){/*Ignore*/}
        try {output.close();}catch(Throwable e){/*Ignore*/}
    }
}

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

يؤدي استخدام أخيرًا إلى نقل مسؤولية الاستخدام الصحيح إلى مستخدم الكائن.باستخدام آلية المنشئ/المدمر التي توفرها C++، يمكنك نقل مسؤولية الاستخدام الصحيح إلى المصمم/المنفذ للفئة.يعد هذا أكثر أمانًا بالوراثة حيث يحتاج المصمم فقط إلى القيام بذلك بشكل صحيح مرة واحدة على مستوى الفصل (بدلاً من مطالبة مستخدمين مختلفين بمحاولة القيام بذلك بشكل صحيح بطرق مختلفة).

باستخدام C++ 11 مع تعبيرات لامدا, لقد بدأت مؤخرًا باستخدام الكود التالي للتقليد finally:

class FinallyGuard {
private:
  std::function<void()> f_;
public:
  FinallyGuard(std::function<void()> f) : f_(f) { }
  ~FinallyGuard() { f_(); }
};

void foo() {
  // Code before the try/finally goes here
  { // Open a new scope for the try/finally
    FinallyGuard signalEndGuard([&]{
      // Code for the finally block goes here
    });
    // Code for the try block goes here
  } // End scope, will call destructor of FinallyGuard
  // Code after the try/finally goes here
}

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

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

بقدر ما أفهم الأشياء، std::function<void()> سيستخدم بعض الاتجاه غير المباشر للمؤشر واستدعاء دالة افتراضية واحدة على الأقل لتنفيذه نوع المحو, ، لذلك سيكون هناك الأداء الزائد.لا تستخدم هذه التقنية في حلقة ضيقة حيث يكون الأداء أمرًا بالغ الأهمية.في تلك الحالات، سيكون الكائن المتخصص الذي يقوم مدمره بشيء واحد فقط أكثر ملاءمة.

يتم إنشاء مدمرات C++ finally متكرر.يمكنك الحصول على نفس التأثير عن طريق نقل كود التنظيف من أدوات التدمير المقابلة أخيرًا.

أعتقد أنك تفتقد المغزى من ماذا catch (...) يقدر على.

أنت تقول في المثال الخاص بك "للأسف، لا يمكن فحص الاستثناء".حسنًا، ليس لديك معلومات حول نوع الاستثناء.أنت لا تعرف حتى ما إذا كان هذا النوع متعدد الأشكال، لذا حتى لو كان لديك نوع من الإشارة غير المكتوبة إليه، فلن تتمكن حتى من محاولة إجراء ذلك بشكل آمن dynamic_cast.

إذا كنت تعرف بعض الاستثناءات أو التسلسلات الهرمية للاستثناءات التي يمكنك القيام بشيء ما بها، فهذا هو المكان المناسب لكتل ​​الالتقاط ذات الأنواع المحددة بشكل صريح.

catch (...) ليس مفيدًا غالبًا في لغة C++.يمكن استخدامه في الأماكن التي يجب أن تضمن عدم رميها، أو رمي بعض الاستثناءات المتعاقد عليها فقط.إذا كنت تستخدم catch (...) للتنظيف، هناك احتمال كبير جدًا أن الكود الخاص بك ليس آمنًا بشكل قوي في أي حال.

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

للإجابة على سؤالك الأصلي، إذا كنت بحاجة إلى جزء من التعليمات البرمجية ليتم تشغيله في نهاية الكتلة، سواء كان ذلك استثناءً أم لا، فستكون الوصفة هي ذلك.

class LocalFinallyReplacement {
    ~LocalFinallyReplacement() { /* Finally code goes here */ }
};
// ...
{ // some function...
    LocalFinallyReplacement lfr; // must be a named object

    // do something
}

لاحظ كيف يمكننا التخلص منها تمامًا try, catch و throw.

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

ليس خارج الموضوع تماما.

تنظيف موارد قاعدة بيانات طلاء الغلايات في Java

وضع السخرية:أليس لغة جافا رائعة؟

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

لقد قمت بمعظم ترميز C++ الخاص بي على نظام التشغيل Windows لذا يمكنني دائمًا اللجوء إلى استخدام __try/__أخيرًا من Microsoft لمثل هذه المواقف.(تتمتع معالجة الاستثناءات المنظمة ببعض القدرات القوية للتفاعل مع الاستثناءات.) للأسف، لا يبدو أن لغة C قد صدقت على الإطلاق على أي بنيات محمولة لمعالجة الاستثناءات.

ومع ذلك، لم يكن هذا حلاً مثاليًا، لأنه لم يكن من السهل دمج كود C وC++ في كتلة محاولة حيث قد يتم طرح أي من نمطي الاستثناء.كان من الممكن أن تكون الكتلة المضافة أخيرًا إلى C++ مفيدة لتلك المواقف وستتيح إمكانية النقل.

فيما يتعلق بتحرير الملحق الخاص بك، نعم يتم النظر في عمليات الإغلاق لـ C++ 0x.يمكن استخدامها مع واقيات نطاق RAII لتوفير حل سهل الاستخدام والتحقق مدونة بيزر.يمكن استخدامها أيضًا لتقليد المحاولة النهائية، انظر هذه الإجابة ;لكن هل هذه فكرة جيدة حقا ؟ .

أعتقد أنني سأضيف الحل الخاص بي لهذا - وهو نوع من غلاف المؤشر الذكي عندما يتعين عليك التعامل مع أنواع غير RAII.

تستخدم مثل هذا:

Finaliser< IMAPITable, Releaser > contentsTable;
// now contentsTable can be used as if it were of type IMAPITable*,
// but will be automatically released when it goes out of scope.

إذن هذا هو تنفيذ Finaliser:

/*  Finaliser
    Wrap an object and run some action on it when it runs out of scope.
    (A kind of 'finally.')
    * T: type of wrapped object.
    * R: type of a 'releaser' (class providing static void release( T* object )). */
template< class T, class R >
class Finaliser
{
private:
    T* object_;

public:
    explicit Finaliser( T* object = NULL )
    {
        object_ = object;
    }

    ~Finaliser() throw()
    {
        release();
    }

    Finaliser< T, R >& operator=( T* object )
    {
        if (object_ != object && object_ != NULL)
        {
            release();
        }
        object_ = object;

        return *this;
    }

    T* operator->() const
    {
        return object_;
    }

    T** operator&()
    {
        return &object_;
    }

    operator T*()
    {
        return object_;
    }

private:
    void release() throw()
    {
        R::release< T >( object_ );
    }
};

...وهنا المحرر:

/*  Releaser
    Calls Release() on the object (for use with Finaliser). */
class Releaser
{
public:
    template< class T > static void release( T* object )
    {
        if (object != NULL)
        {
            object->Release();
        }
    }
};

لدي عدة أنواع مختلفة من أدوات التحرير مثل هذه، بما في ذلك واحدة مجانية () وواحدة لـ CloseHandle ().

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