هل يجب استخدام التصفيات غير المجدية على أنواع الإرجاع ، للتوضيح؟

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

سؤال

تشكو أداة التحليل الثابت لدينا من "مؤهل نوع غير مجدي على نوع الإرجاع" عندما يكون لدينا نماذج أولية في ملفات الرأس مثل:

const int foo();

لقد حددنا ذلك بهذه الطريقة لأن الوظيفة تعود إلى ثابت لن يتغير أبدًا ، معتقدين أن واجهة برمجة التطبيقات تبدو أكثر وضوحًا const في المكان.

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

سؤالي هو ، هل هناك أي سبب قد يسبب مشكلة؟ هل يجب أن نتجاهل الأخطاء الناتجة عن الأداة ، أم هل يجب أن نضعف الأداة بتكلفة محتملة لواجهة برمجة تطبيقات أقل وضوحًا واتساقًا؟ (يعود آخر const char* الثوابت التي لا تواجه الأداة مشكلة معها.)

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

المحلول

من الأفضل عادة أن تصف التعليمات البرمجية الخاصة بك بشكل دقيق قدر الإمكان. أنت تحصل على هذا التحذير لأن const في const int foo(); لا معنى له بشكل أساسي. تبدو واجهة برمجة التطبيقات أكثر وضوحًا فقط إذا كنت لا تعرف ماذا const الكلمة الرئيسية تعني. لا تفرط في الحمولة المعنى من هذا القبيل ؛ static سيء بما فيه الكفاية كما هو ، وليس هناك سبب لإضافة احتمال لمزيد من الارتباك.

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

const int foo();
char * const foo2();

كلاهما سيؤدي إلى تحليلك الثابت لإعطاء التحذير - إضافة مؤهل const إلى قيمة الإرجاع هو عملية لا معنى لها. من المنطقي فقط عندما يكون لديك معلمة مرجعية AA (أو نوع الإرجاع) ، مثل const char * مثال.

في الواقع ، لقد أجريت للتو برنامج اختبار صغير ، وحذر GCC بشكل صريح من هذه المشكلة:

test.c:6: warning: type qualifiers ignored on function return type

لذلك ليس فقط برنامج التحليل الثابت الخاص بك هو الشكوى.

نصائح أخرى

يمكنك استخدام تقنية مختلفة لتوضيح نيتك دون جعل الأدوات غير سعيدة.

#define CONST_RETURN

CONST_RETURN int foo();

ليس لديك مشكلة مع const char * لأن هذا يعلن مؤشرًا إلى chars المستمرة ، وليس مؤشرًا ثابتًا.

تجاهل const الى الان، foo() إرجاع القيمة. يمكنك ان تفعل

int x = foo();

وتعيين القيمة التي تم إرجاعها بواسطة foo() إلى المتغير x, ، بنفس الطريقة التي يمكنك القيام بها

int x = 42;

لتعيين القيمة 42 إلى متغير x.
لكن لا يمكنك تغيير 42 ... أو القيمة التي تم إرجاعها بواسطة foo(). قائلا أن القيمة عادت من foo() لا يمكن تغييرها ، من خلال تطبيق const الكلمة الرئيسية لنوع foo() لا ينجز شيئًا.

قيم لا يمكن const (أو restrict, ، أو volatile). الكائنات فقط يمكن أن يكون لها التصفيات نوع.


على النقيض من

const char *foo();

في هذه الحالة، foo() إرجاع مؤشر إلى كائن. يمكن أن يكون الكائن الذي يشير إليه القيمة التي تم إرجاعها مؤهلاً const.

يتم إرجاع INT بواسطة ينسخ. قد تكون نسخة من const ، ولكن عندما يتم تعيينها لشيء آخر ، أن شيئًا بحكم حقيقة أنه كان قابلًا للتخصيص ، لا يمكن بحكم التعريف كونه.

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

const int foo() مختلف جدا عن const char* foo(). const char* foo() إرجاع صفيف (عادة ما يكون سلسلة) لا يُسمح للمحتوا به بالتغيير. فكر في الفرق بين:

 const char* a = "Hello World";

و

const int b = 1;

a لا يزال متغيرًا ويمكن تعيينه إلى سلاسل أخرى لا يمكن أن تتغير في حين b ليس متغير. لذا

const char* foo();
const char* a = "Hello World\n";
a = foo();

مسموح به ولكن

const int bar();
const int b = 0;
b = bar();

غير مسموح به ، حتى مع const إعلان عن bar().

نعم. أود أن أنصح كتابة رمز "بشكل صريح" ، لأنه يوضح أي شخص (بما في ذلك نفسك) عند قراءة الكود الذي تعنيه. أنت تكتب رمز ل مبرمجون آخرون للقراءة, ، عدم إرضاء نزوات المترجم وأدوات التحليل الثابت!

(ومع ذلك ، يجب أن تكون حذراً من أن أي "رمز غير ضروري" لا يتسبب في إنشاء رمز مختلف!)

بعض الأمثلة على الترميز الصريح تحسين قابلية القراءة/الصيانة:

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

    int a = b + c * d / e + f;      // Hard to read- need to know precedence
    int a = b + ((c * d) / e) + f;  // Easy to read- clear explicit calculations
    

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

    virtual int MyFunc()
    وهذا يجعل من الواضح لأي شخص يقرأ رأسك في الفصل أن هذه الطريقة افتراضية. (تم إصلاح "خلل بناء الجملة C ++ هذا" في C# من خلال طلب استخدام "تجاوز" الكلمة الرئيسية في هذه الحالة - المزيد من الإثبات إذا كان أي شخص بحاجة إلى أن تفقد "الافتراضية غير الضرورية" فكرة سيئة حقًا)

هذه أمثلة واضحة حيث يؤدي إضافة رمز "غير ضروري" إلى جعل الرمز أكثر قابلية للقراءة وأقل عرضة للأخطاء.

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