ما هي بعض التعابير والمفاهيم الخاطئة والمشاكل المرتبطة بـ C++ والتي تعلمتها من تجربتك؟

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

  •  08-07-2019
  •  | 
  •  

سؤال

ما هي بعض التعابير والمفاهيم الخاطئة والمشاكل المرتبطة بـ C++ والتي تعلمتها من تجربتك؟

مثال:

class A
{
  public: 
  char s[1024];
  char *p;

  A::A()
  {
    p = s;
  }

  void changeS() const
  {
    p[0] = 'a';
  }

};

حتى أن التغييرات هي وظيفة عضو ثابت، فهي تغير قيمة الكائن.لذا فإن وظيفة العضو const تعني فقط أنها ستعامل جميع المتغيرات على أنها const، وهذا لا يعني أنها ستحتفظ فعليًا بجميع الأعضاء const.(لماذا؟الكلمة الأساسية const في وظيفة العضو تعامل char *p؛مثل شار * const p؛وليس كـ const char *p;

مما يعني أن p لا يمكن أن يشير إلى شيء آخر.وليس أنه لا يمكنك تغيير بيانات p.

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

المحلول

ولقد أحببت هذا منذ زمن لقد اكتشفت في بعض رمز:

assert(condition || !"Something has gone wrong!");

وأو إذا لم يكن لديك حالة في متناول اليد، يمكنك القيام به فقط

assert(!"Something has gone wrong!");

ويعزى التالية لJosh (انظر التعليقات). ويستخدم فاصلة المشغل بدلا من ذلك:

assert(("Something has gone wrong!", condition)); 

نصائح أخرى

وأنت لست بحاجة لمعرفة وظيفة معقدة بناء جملة تعريف typedef وC ++ الصورة. إليك خدعة لطيف وجدت.

والسريع، ووصف هذا typedef و:

typedef C &(__cdecl C::* const CB )(const C &) const;

وسهلة! CB هو مؤشر إلى دالة عضو من الفئة C قبول إشارة CONST إلى كائن C والعودة إشارة غير CONST إلى كائن C. أوه، وانها وظيفة عضو CONST. أوه، ومؤشر الدالة نفسه CONST ... (أليس كذلك؟)

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

struct C {
        const C& Callback(const C&) const   { }
};

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

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

char c = &C::Callback;

والمترجم لحسن الحظ ينفث رسالة خطأ مفيدة هذا:

“… cannot convert from 'const C &(__cdecl C::* )(const C &) const' to 'char'”

وهذا ما نبحث عنه. :)

وهنا هو آخر واحد مسكت في يوم من الأيام:

char int2hex(int x) {
     return "-0123456789abcdef"[(x >= 0 && x < 16) ? (x + 1) : 0];
}

وانها مجرد فهرسة صفيف حرف بدلا من القيام التبديل. لو كان خارج النطاق، فإنه يعود '-'.

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

في بعض الأحيان، وتلوث رؤوس مع أسماء لا تتصرف مثل الكلية

#define max(a, b) (a > b ? a : b)

والتي سوف تجعل غير صالحة التعليمات البرمجية التي تستخدم وظيفة أو وظيفة الكائن ماكس دعا بهذه الطريقة. وwindows.h مثال سيء السمعة الذي يفعل ذلك بالضبط. طريقة واحدة حوله هو وضع أقواس حول الدعوة التي تتوقف عن استخدام الماكرو ويجعل من استخدام ماكس وظيفة حقيقية:

void myfunction() {
    ....
    (max)(c, d);
}

والآن، والحد الأقصى هو بين قوسين ولا تحسب على أنها دعوة إلى الماكرو بعد الآن!

واحد نادرا ما تستخدم، ولكن مفيد C ++ لغة هو استخدام:؟ المشغل خلال سلسلة منشئ

class Sample
{  
    const char * ptr;
    const bool  freeable;

    Sample(const char * optional):
        ptr( optional ? optional : new char [32]),
        freeable( optional ? false : true ) {}
    ~Sample( )  { if (freeable) delete[] ptr; }
}  

وC ++ لا يسمح القيم CONST إلى تغيير داخل الجسم من المنشئ، لذلك هذا يتجنب CONST-يلقي.

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

وهناك عدد قليل من الأشياء التي عادة ما تكون رحلة الشعب الأعلى:

std::cout << a << a++ << --a;
i = ++i;

وهذه السطور على حد سواء غير محددة.

void foo(bar* b1, bar* b2);

int main() {
  foo(shared_ptr<bar>(new bar()), shared_ptr<bar>(new bar()));
}

ما سبق قد تسرب الذاكرة.

int* arr = new int[10];
arr + 11;

وهذا يؤدي إلى سلوك غير معرف.

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

وبما أننا جميعا تجاهل OP، وبدلا من نشر لدينا بارد الحيل المفضلة ...

استخدم دفعة (أو TR1) shared_ptr للحفاظ على درجة ثابتة في وقت التشغيل (نوع من اضحة، ولكن أنا لم أر أي شخص آخر القيام بذلك):

#include <cassert>
#include <functional>
#include <stdexcept>
#include <boost/shared_ptr.hpp>
using namespace std;
using namespace boost;

class Foo
{
public:
    Foo() : even(0)
    {
        // Check on start up...
        Invariant();
    }

    void BrokenFunc()
    {
        // ...and on exit from public non-const member functions.
        // Any more is wasteful.
        shared_ptr<Foo> checker(this, mem_fun(&Foo::Invariant));

        even += 1;
        throw runtime_error("didn't expect this!");
        even += 1;
    }

private:
    void Invariant() { assert(even % 2 == 0); }
    int even;
};

ومنذ علمت عن RAII (واحد من أسوأ المختصرات أي وقت مضى) ومؤشر الذكية، اختفت الذاكرة والموارد التسريبات تماما تقريبا.

إذا كان لديك فصل لا يحتوي على دلالات قيمة، فتأكد من أن جميع التركيبات التالية موجودة صراحة أعلن من أجل منع الصداع على الطريق.

  • المنشئ الافتراضي
  • نسخ المنشئ
  • مهمة تشغيل

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

من المفيد أيضًا إضافة تعليق في الأعلى يوضح أن هذه ليست فئة آمنة للنسخ.

هذا سوف يوفر لك الوقت على الطريق.

وأنا لا أستطيع أن أقول أنا C ++ ذوي الخبرة مبرمج لكني علمت مؤخرا كيف أنه من الصعب أن يمر ومجموعة من المصفوفات كمعلمة وظيفة. في محاولة لتجنب هذا بأي ثمن: (

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

m[i*dim2+j]

ويجري لي مكرر للصفوف، dim2 عدد من الأعمدة وي المكرر لالعواميد.

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

#include <iostream>
using namespace std;

int& strangeFunction(int& x){return x;}


int main(){
        int a=0;
        strangeFunction(a) = 5;               //<------- I found this very confusing
        cout << a <<endl;
        return 0;
}

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

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