هل هناك فائدة عملية لإلقاء مؤشر فارغ إلى كائن ما والاتصال بأحد وظائف الأعضاء؟

StackOverflow https://stackoverflow.com/questions/2569602

سؤال

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

#ifndef offsetof
#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field))
#endif

يمكن للمرء أن يناقش ما إذا كان استغلال مثل هذه اللغة الدقيقة في مثل هذه الحالة صالحة أم لا ، أو حتى ضروري ، لكنني رأيت أيضًا أنه يستخدم مثل هذا:

struct Result
{
   void stat()
   {
      if(this)
         // do something...
      else
         // do something else...
   }
};

// ...somewhere else in the code...

((Result*)0)->stat();

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

ملاحظة. آسف على الممثلين على غرار C ، للأسف لا يزال الناس يفضلون الكتابة بشكل أقل إذا أمكنهم ذلك.

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

المحلول

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

الحالة الثانية سخيفة بشكل معقول. الشيء المعقول هو إعلان هذه الطريقة ثابتة.

نصائح أخرى

لا أرى أي فائدة ((Result*)0)->stat(); - إنه اختراق قبيح من المحتمل أن ينكسر عاجلاً وليس آجلاً. سيكون نهج C ++ المناسب هو استخدام طريقة ثابتة Result::stat() .

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

قام الجميع بعمل جيد في التأكيد على أن السلوك غير محدد. ولكن دعنا نتظاهر أنه لم يكن كذلك p->member يُسمح له بالتصرف بطريقة متسقة في ظل ظروف معينة حتى لو p ليس مؤشرًا صالحًا.

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

من منظور السلامة ، لم تقم بحماية سوى جزء صغير من الطرق غير صالحة this يمكن إنشاء المؤشر. هناك مؤشرات غير مؤهلة ، للمبتدئين:

Result* p;
p->stat(); //Oops, 'this' is some random value

هناك مؤشرات تمت تهيئتها ، لكنها لا تزال غير صالحة:

Result* p = new Result;
delete p;
p->stat(); //'this' points to "safe" memory, but the data doesn't belong to you

وحتى إذا قمت دائمًا بتهيئة مؤشراتك ، ولم تعيد استخدام ذاكرة الحرة عن طريق الخطأ:

struct Struct {
    int i;
    Result r;
}
int main() {
    ((Struct*)0)->r.stat(); //'this' is likely sizeof(int), not 0
}

في الحقيقة ، حتى لو لم يكن سلوكًا غير محدد ، فهو سلوك لا قيمة له.

على الرغم من أن المكتبات التي تستهدف تطبيقات C ++ محددة قد تفعل ذلك ، فإن هذا لا يعني أنها "شرعية" بشكل عام.

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

لا ، لأنه على الرغم من أنه قد يعمل بشكل جيد على بعض تطبيقات C ++ ، إلا أنه من الجيد تمامًا عدم العمل على أي تطبيق C ++ المطابق.

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

لمجرد أنه لا يعطل على الفور في حالة اختبار محددة لا يعني أنه لن يدفعك إلى جميع أنواع المتاعب.

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

بخصوص البيان:

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

الكود ليس مشروعا. ليس هناك ما يضمن أن المترجم و/أو وقت التشغيل سيتصل بالفعل بالطريقة عندما يكون المؤشر فارغًا. لا يتم تسجيل الطريقة في هذه الطريقة لأنه لا يمكنك افتراض أن الطريقة ستنتهي فعليًا معها مع أ NULL this مؤشر.

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