الجوانب الإشكالية للبرمجة في C ++


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




جدول المحتويات


جدول المحتويات

مقدمة
1. الأنواع
1.1. التعليمات والمشغلين الشرطيين
1.2. التحويلات الضمنية
2. تحليل الاسم
2.1. إخفاء المتغيرات في النطاقات المتداخلة
2.2. وظيفة الزائد
3. المُنشئون ، المدمرون ، التهيئة ، الحذف
3.1. دالات عضو الفئة التي تم إنشاؤها بواسطة المترجم
3.2. متغيرات غير مهيأة
3.3. إجراء التهيئة للفصول الأساسية وأعضاء الفئة غير الثابتة
3.4. إجراء التهيئة لأعضاء الفئة الثابتة والمتغيرات العالمية
3.5. الاستثناءات في المدمرات
3.6. إزالة الكائنات والمصفوفات الديناميكية
3.7. الحذف عندما يكون تصريح الفصل الدراسي غير مكتمل
4. عوامل التشغيل والتعبيرات
4.1. أولوية عامل التشغيل
4.2. عامل التحميل الزائد
4.3. إجراء حساب التعبيرات الفرعية
5. وظائف افتراضية
5.1 تجاوز الوظائف الافتراضية
5.2 التحميل الزائد واستخدام المعلمات الافتراضية
5.3 استدعاء الوظائف الافتراضية في المُنشئ والمدمّر
5.4 المدمر الظاهري
6. العمل المباشر مع الذاكرة
6.1 تفيض العازلة
6.2 سلاسل منتهية بحرف Z
6.3 وظائف مع عدد متغير من المعلمات
7. النحو
7.1 الإعلانات المعقدة
7.2 غموض النحو
8. متفرقات
8.1 الكلمات الرئيسية المضمنة و ODR
8.2 ملفات الرأس
8.3 بيان التبديل
8.4 تمرير المعلمات حسب القيمة
8.5 إدارة الموارد
8.6 روابط التملك وغير الحيازة
8.7 التوافق الثنائي
8.8 وحدات الماكرو
9. الملخص
المراجع



Praemonitus ، praemunitus.
تحذير مسبق يعني مسلح. (خط الطول).



مقدمة


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



1. الأنواع



1.1. التعليمات والمشغلين الشرطيين


تؤدي الحاجة إلى التوافق مع C إلى حقيقة أنه في العبارة if(...) وما شابه ، يمكنك استبدال أي تعبير رقمي أو مؤشر ، وليس مجرد تعبيرات مثل bool . وتتفاقم المشكلة من خلال التحويل الضمني من bool إلى int في التعبيرات الحسابية وأولوية بعض عوامل التشغيل. وهذا يؤدي ، على سبيل المثال ، إلى الأخطاء التالية:


if(a=b) عندما يكون صحيحًا if(a==b) ،
if(a<x<b) ، عندما يكون صحيحًا if(a<x && x<b) ،
if(a&x==0) ، عندما يكون صحيحًا if((a&x)==0) ،
if(Foo) صحيحًا if(Foo()) ،
if(arr) عندما يكون صحيحًا if(arr[0]) ،
if(strcmp(s,r)) عند تصحيحها if(strcmp(s,r)==0) .


تتسبب بعض هذه الأخطاء في تحذير مترجم ، ولكن ليس خطأ. يمكن أن يساعد محللو الكود أحيانًا. في C # ، تكاد تكون هذه الأخطاء مستحيلة ، if(...) وتعليمات مماثلة تتطلب نوعًا bool ، فلا يمكنك مزج الأنواع bool في التعبيرات الحسابية.


كيفية القتال:


  • برنامج بدون تحذيرات. لسوء الحظ ، هذا لا يساعد دائمًا ؛ بعض الأخطاء الموضحة أعلاه لا تعطي تحذيرات.
  • استخدم محللات التعليمات البرمجية الثابتة.
  • تقنية الاستقبال القديمة: عند المقارنة بثابت ، ضعها على اليسار ، على سبيل المثال if(MAX_PATH==x) . إنها تبدو جميلة جدًا (وحتى لها اسمها الخاص - "تدوين Yoda") ، وتساعد في عدد قليل من الحالات التي تم النظر فيها.
  • استخدم const على أوسع نطاق ممكن. مرة أخرى ، لا يساعد ذلك دائمًا.
  • اعتد على كتابة التعبيرات المنطقية الصحيحة: if(x!=0) بدلاً من if(x) . (على الرغم من أنه يمكنك الوقوع في فخ أولويات المشغل هنا ، انظر المثال الثالث.)
  • كن منتبهًا للغاية.


1.2. التحويلات الضمنية


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


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


يمكن للمبرمج إضافة تحويلات ضمنية أخرى: (1) تحديد مُنشئ بمعلمة واحدة بدون الكلمة الأساسية explicit ؛ (2) تعريف عامل تحويل النوع. تكسر هذه التحولات ثغرات أمنية إضافية بناءً على مبادئ الكتابة القوية.


في C # ، يكون عدد التحويلات الضمنية المضمنة أصغر بكثير ؛ يجب الإعلان عن التحويلات الضمنية المخصصة باستخدام الكلمة الأساسية implicit .


كيفية القتال:


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


2. تحليل الاسم



2.1. إخفاء المتغيرات في النطاقات المتداخلة


في C ++ ، تنطبق القاعدة التالية. دع


 //   {    int x;    // ... //  ,       {        int x;        // ...    } } 

وفقًا لقواعد C ++ ، فإن المتغير المعلن في يخفي المتغير المعلن عنه في لا يجب أن يكون الإعلان الأول x في كتلة: يمكن أن يكون عضوًا في فئة أو متغير عام ، بل يجب أن يكون مرئيًا في الكتلة


تخيل الموقف الآن عندما تحتاج إلى إعادة صياغة الكود التالي


 //   {    int x;    // ... //  ,       {    // -         } } 

عن طريق الخطأ ، يتم إجراء التغييرات:


 //   {    //  , :    int x;    // -         // ...    //  :    // -      } 

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


كيفية القتال:


  • قم بتعريف المتغيرات في أضيق نطاق ممكن.
  • لا تكتب كتل طويلة ومتداخلة بعمق.
  • استخدم اصطلاحات التشفير للتمييز المرئي للمعرفات ذات النطاق المختلف.
  • كن منتبهًا للغاية.


2.2. وظيفة الزائد


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


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


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


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


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


بحذر شديد ، يجب استخدام الحمل الزائد والمعلمات الافتراضية للوظائف الافتراضية ، انظر القسم 5.2.


يدعم C # أيضًا التحميل الزائد للوظائف ، لكن قواعد حل التحميل الزائد تختلف قليلاً.


كيفية القتال:


  • لا تسيء استخدام التحميل الزائد للوظائف ، وكذلك تصميم الوظائف باستخدام المعلمات الافتراضية.
  • إذا كانت الوظائف محملة بشكل زائد ، فاستخدم التوقيعات التي لا تكون موضع شك عند حل الأحمال الزائدة.
  • لا تعلن وظائف بنفس الاسم في النطاق المتداخل.
  • لا تنس أن آلية الوظائف عن بعد ( =delete ) التي ظهرت في C ++ 11 يمكن استخدامها لحظر بعض خيارات التحميل الزائد.


3. المُنشئون ، المدمرون ، التهيئة ، الحذف



3.1. دالات عضو الفئة التي تم إنشاؤها بواسطة المحول البرمجي


إذا لم يحدد المبرمج وظائف أعضاء الفئة من القائمة التالية - المنشئ الافتراضي ، مُنشئ النسخ ، عامل تعيين النسخ ، المدمر - عندها يمكن للمترجم القيام بذلك من أجله. أضاف C ++ 11 مُنشئ نقل وعامل تعيين نقل إلى هذه القائمة. تسمى وظائف الأعضاء هذه وظائف الأعضاء الخاصة. يتم إنشاؤها فقط إذا تم استخدامها ، ويتم استيفاء الشروط الإضافية الخاصة بكل وظيفة. نلفت الانتباه إلى حقيقة أن هذا الاستخدام قد يتضح أنه مخفي تمامًا (على سبيل المثال ، عند تنفيذ الوراثة). إذا تعذر إنشاء الوظيفة المطلوبة ، فسيتم إنشاء خطأ. (باستثناء عمليات النقل ، يتم استبدالها بعمليات النسخ). وظائف الأعضاء التي تم إنشاؤها بواسطة المترجم عامة ويمكن تضمينها. يمكن الاطلاع على تفاصيل وظائف الأعضاء الخاصة في [Meyers2].


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


يمكن للمبرمج منع إنشاء وظائف الأعضاء الخاصة ، في C ++ 11 من الضروري استخدام بناء "=delete" عند التصريح ، في C ++ 98 الإعلان عن وظيفة العضو المقابلة الخاصة وعدم تعريفها.


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


في C # ، يمكن أن يولد المحول البرمجي مُنشئًا افتراضيًا ، عادةً لا يسبب هذا أي مشاكل.


كيفية القتال:


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


3.2. متغيرات غير مهيأة


يمكن تسمية المنشئين والمدمرين بالعناصر الأساسية لنموذج كائن C ++. عند إنشاء كائن ، يجب استدعاء المنشئ ، وعند الحذف ، يتم استدعاء المدمر. لكن مشكلات التوافق مع C فرضت بعض الاستثناءات ، ويسمى هذا الاستثناء الأنواع التافهة. يتم تقديمها لمحاكاة أنواع sichny ودورة حياة syshny من المتغيرات ، دون استدعاء إلزامي من المنشئ والمدمّر. كود C ، إذا تم تجميعه وتنفيذه في C ++ ، يجب أن يعمل تمامًا كما هو الحال في C. تتضمن الأنواع التافهة الأنواع الرقمية ، والمؤشرات ، والتعداد ، بالإضافة إلى الفئات والهياكل والنقابات والمصفوفات التي تتكون من أنواع تافهة. يجب أن تستوفي الفئات والهياكل بعض الشروط الإضافية: عدم وجود مُنشئ مخصص ، مُدمِّر ، نسخ ، وظائف افتراضية. بالنسبة لفئة تافهة ، يمكن للمترجم إنشاء مُنشئ افتراضي ومدمر. يقوم المُنشئ الافتراضي بتصفير الكائن ، ولا يقوم المدمر بأي شيء. ولكن سيتم إنشاء هذا المُنشئ واستخدامه فقط إذا تم استدعاؤه بشكل صريح عند تهيئة المتغير. لن يتم تحديد متغير من نوع تافه إذا لم تستخدم نوعًا مختلفًا من التهيئة الصريحة. تعتمد صيغة التهيئة على نوع وسياق تعريف المتغير. تتم تهيئة المتغيرات الثابتة والمحلية عند الإعلان عنها. بالنسبة للفصل ، تتم تهيئة الفئات الأساسية الفورية وأعضاء الفئة غير الثابتة في قائمة تهيئة المنشئ. (C ++ 11 يسمح لك بتهيئة أعضاء الفئة غير الثابتة عند الإعلان ، انظر لاحقًا.) بالنسبة للكائنات الديناميكية ، يقوم التعبير new T() بإنشاء كائن تمت تهيئته بواسطة المُنشئ الافتراضي ، لكن new T للأنواع التافهة يُنشئ كائنًا غير مهيأ. عند إنشاء صفيف ديناميكي من نوع تافه ، new T[N] ، ستكون عناصره دائمًا غير مهيأة. إذا تم إنشاء أو توسيع مثيل std::vector<T> ولم يتم توفير المعلمات من أجل التهيئة الصريحة للعناصر ، عندئذٍ يتم ضمان استدعاء المُنشئ الافتراضي. يقدم C ++ 11 صيغة جديدة للتهيئة - باستخدام الأقواس المتعرجة. يعني وجود زوج من الأقواس الفارغة التهيئة باستخدام المُنشئ الافتراضي. هذا التهيئة ممكن في كل مكان حيث يتم استخدام التهيئة التقليدية ، إلى جانب أنه أصبح من الممكن تهيئة أعضاء غير ثابتين في الفصل عند الإعلان ، والذي يحل محل التهيئة في قائمة التهيئة للمنشئ.


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


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


تحتوي مكتبة C ++ 11 القياسية على قوالب تسمى خصائص النوع (ملف الرأس <type_traits> ). يسمح لك أحدها بتحديد ما إذا كان النوع تافهًا. يكون التعبير std::is_trivial<>::value true إذا كان T تافهًا و false .


غالبًا ما تسمى الهياكل Sysylic البيانات القديمة البسيطة (POD). يمكننا أن نفترض أن POD و "النوع التافه" هما مصطلحان متكافئان تقريبًا.


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


كيفية القتال:


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


3.3. إجراء التهيئة للفصول الأساسية وأعضاء الفئة غير الثابتة


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


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


كيفية القتال:


  • احتفظ بقائمة تهيئة المنشئ بترتيب التصريح.
  • حاول أن تجعل تهيئة الطبقات الأساسية وأعضاء الفصل مستقلة.
  • استخدم التهيئة للأعضاء غير الثابتة عند الإعلان.


3.4. إجراء التهيئة لأعضاء الفئة الثابتة والمتغيرات العالمية


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


كيفية القتال:


  • اتخاذ تدابير خاصة لمنع هذا الوضع. على سبيل المثال ، استخدم المتغيرات الثابتة المحلية (singleton) ، يتم تهيئتها عند الاستخدام الأول.


3.5. الاستثناءات في المدمرات


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


كيفية القتال:


  • تجنب رمي الاستثناءات في المدمر.


3.6. إزالة الكائنات والمصفوفات الديناميكية


إذا تم إنشاء كائن ديناميكي من نوع T


 T* pt = new T(/* ... */); 

ثم يتم حذفه باستخدام عامل delete


 delete pt; 

إذا تم إنشاء مصفوفة ديناميكية


 T* pt = new T[N]; 

ثم يتم حذفه باستخدام عامل delete[]


 delete[] pt; 

إذا لم تتبع هذه القاعدة ، يمكنك الحصول على سلوك غير محدد ، أي أنه يمكن أن يحدث أي شيء: تسرب للذاكرة ، أو تعطل ، وما إلى ذلك. راجع [Meyers1] للحصول على التفاصيل.


كيفية القتال:


  • استخدم نموذج delete الصحيح.


3.7. الحذف عندما يكون تصريح الفصل الدراسي غير مكتمل


يمكن أن تثير عامل عامل delete النهمة مشاكل معينة ؛ يمكن تطبيقها على مؤشر من نوع void* أو على مؤشر لفئة لديها تصريح غير مكتمل (استباقي). عامل delete المطبق على مؤشر على فئة ما هو عملية من مرحلتين ؛ أولاً ، يتم استدعاء المدمر ، ثم يتم تحرير الذاكرة. delete , ( ). فكر في مثال:


 class X; //   X* CreateX(); void Foo() {    X* p = CreateX();    delete p; } 

, , delete X . Visual Studio :

warning C4150: deletion of pointer to incomplete type 'X'; no destructor called


X CreateX() , , CreateX() , new , Foo() , . , , .


, -. , . , , , , . [Meyers2].


:


  • .
  • .
  • .


4. ,



4.1.


++ , . . . , 1.1.


هنا مثال:


 std::out<<c?x:y; 


 (std::out<<c)?x:y; 


 std::out<<(c?x:y); 

, , .


. << ?: std::out void* . ++ , . -, , . ?: . , ( ).


: x&f==0 x&(f==0) , (x&f)==0 , , , . - , , , , .


. / . / , /, . , x/4+1 x>>2+1 , x>>(2+1) , (x>>2)+1 , .


C# , C++, , - .


:


  • , . , , .


4.2.


++ , . . , , . 4.1. — + += . . , : , (), && , || . , (-), (short-circuit evaluation semantics), , . & ( ). & , .. .


, - (-) , . .


- , , . . [Dewhurst].


C# , , , .


:


  • .
  • .


4.3.


++ , . ( : , (), && , || , ?: .) , , , . :


 int x=0; int y=(++x*2)+(++x*3); 

y .


, . .

 class X; class Y; void Foo(std::shared_ptr<X>, std::shared_ptr<Y>); 

Foo() :


 Foo(std::shared_ptr<X>(new X()), std::shared_ptr<Y>(new Y())); 

: X , Y , std::shared_ptr<X> , std::shared_ptr<Y> . Y , X .


:


 auto p1 = std::shared_ptr<X>(new X()); auto p2 = std::shared_ptr<Y>(new Y()); Foo(p1, p2); 

std::make_shared<Y> ( , ):


 Foo(std::make_shared<X>(), std::make_shared<Y>()); 

. [Meyers2].


:


  • .


5.



5.1.


++98 , ( ), , ( , ). virtual , , . ( ), , , . , , . , ++11 override , , , . .


:


  • override .
  • . , .


5.2.


. , , . . . [Dewhurst].


:


  • .


5.3.


, , . , , post_construct pre_destroy. , — . . , : ( ) . (, , .) , ( ), ( ). . [Dewhurst]. , , .


— - .


, C# , , , . C# : , , . , ( , ).


:


  • , , .


5.4.


, , delete . , - .


:


  • .


6.


— C/C++, . . . « ».


C# unsafe mode, .



6.1.


/++ , : strcpy() , strcat() , sprinf() , etc. ( std::vector<> , etc.) , . (, , , . . Checked Iterators MSDN.) , : , , ; , .


C#, unsafe mode, .


:


  • , .
  • .
  • z-terminated , _s (. ).


6.2. Z-terminated


, . , :


 strncpy(dst,src,n); 

strlen(src)>=n , dst (, ). , , . . — . if(*str) , if(strlen(str)>0) , . [Spolsky].


C# string .


:


  • .
  • z-terminated , _s (. ).


6.3.


... . printf - , C. , , , , . , .


C# printf , .


:


  • . , printf - /.
  • .


7.



7.1.


++ , , , . هنا مثال:


 const int N = 4, M = 6; int x,                // 1    *px,              // 2    ax[N],            // 3    *apx[N],          // 4    F(char),          // 5    *G(char),          // 6    (*pF)(char),      // 7    (*apF[N])(char),  // 8    (*pax)[N],        // 9    (*apax[M])[N],    // 10    (*H(char))(long);  // 11 

:


  1. int ;
  2. int ;
  3. N int ;
  4. N int ;
  5. , char int ;
  6. , char int ;
  7. , char int ;
  8. N , char int ;
  9. N int ;
  10. M N int ;
  11. , char , long int .

, . ( .)


* & . ( .)


typedef ( using -). , :


 typedef int(*P)(long); PH(char); 

, .


C# , .


:


  • .


7.2.


.


 class X { public:    X(int val = 0); // ... }; 


 X x(5); 

x X , 5.


 X x(); 

x , X , x X , . X , , :


 X x; X x = X(); X x{};    //   C++11 

, , , . [Sutter].


, , C++ ( ). . ( C++ .)


, , , , .


C# , , .


:


  • .


8.



8.1. inline ODR


, inline — . , . inline (One Defenition Rule, ODR). . , . , ODR. static : , , . static inline . , , ODR, . , . - , -. .


:


  • «» inline . namespace . , .
  • namespace .


8.2.


. . , , , , .


:


  • , .
  • , : () , -.
  • using -: using namespace , using -.
  • .


8.3. switch


break case . ( .) C# .


:


  • .


8.4.


++ , — , — . ( class struct ) , . ( , # Java.) — , .


  1. , . ( std::string , std::vector , etc.), , .
  2. , , .
  3. , (slicing), , .

, , , . . , , . , . . — ( =delete ), — explicit .


C# , .


:


  • , .
  • .


8.5. إدارة الموارد


++ . , . - ( ), ++11 , , , .


C++ .


C# , . , . (using-) Basic Dispose.


:


  • - .


8.6.


«» . , , C++ , STL- - .


. . , . . «», . COM- . (, .) , C++ . — . . . , («» ) , . .


# , . — .


:


  • .
  • .


8.7.


C++ , : , , . ( !) . , . , . , , . (, .)


C ( ), C++ C ( extern "C" ). C/C++ .


-. #pragma - , , .


, , , .


, , COM. COM-, , ( , ). COM , , .


C# . , — , C#, C# C/C++.


:


  • .


8.8.


, . , . C++ . بدلاً من ذلك


 #define XXL 32 


 const int XXL=32; 

. inline .


# ( ).


:


  • .


9.


  1. . . . , .
  2. .
  3. . ++ — ++11/14/17.
  4. - , - .
  5. .


المراجع


[Dewhurst]
, . C++. .: . . — .: , 2012.


[Meyers1]
, . C++. 55 .: . . — .: , 2014.


[Meyers2]
, . C++: 42 C++11 C++14.: . . — .: «.. », 2016.


[Sutter]
, . C++.: . . — : «.. », 2015.


[Spolsky]
, . .: . . — .: -, 2008.




Source: https://habr.com/ru/post/ar428898/


All Articles