السلام عليكم ,,
أشار الأخ الشمري في موضوع في القسم إلى مقال ينتقد ++C على الرابط التالي :
( شبه لغز ): استخرج الخطأ القاتل في هذا الكود.
و عنوان المقال : Why I Dislike C++ For Large Projects
قرأت المقال و هو جميل حقيقة و لكن لم يعجبني رأي الكاتب, و سأعرض نقدي للمقال آملاً أن يكون بناءً :)
بداية ينتقد كاتب المقال ++C بالقول أنها "شديدة التعقيد" و أن الـ Bugs شبه حتمية عندما يكون المبرمج غير خبير بها.
ثم يعرج على المؤشرات و يقول بأن المشاريع التي تتم غالباً ما تضم مبرمجين خبراء إضافة إلى مبرمجين في أول الطريق, و الخبراء لهم أساليبهم في التعامل مع المؤشرات بكفاءة, و يضيع مبرمجو أول الدرب في شباك المؤشرات. بالطبع لاينسى أن يذكر بأنه عمل في فرق برمجة تضم من أربعين إلى خمسين مبرمجاً, و إحدى تلك المشاريع كانت تتم كتابتها بلغة Java و ++C شبه مناصفة. و من ضمن مهامه في ذلك المشروع كان اختيار أعضاء الفريق الجدد, و معظمهم كما يقول يذكرون خبرةً على الأقل ثلاث سنوات إضافة إلى أنهم قادمون من جامعات جيدة. و كان سؤال تلك المقابلات هو كالتالي :
قم بكتابة صنف يسمى NamedPoint و الذي يضم ثلاث متغيرات... عددان حقيقيان يمثلان x و y على المستوي, إضافة إلى نص ليكون إسماً لتلك النقطة.
يقول بأن معظم الحلول كانت تصب في دوار الآتي :
class NamedPoint
{
private:
float x;
float y;
char *name;
public:
NamedPoint (float x, float y, char *name)
{
this->x = x;
this->y = y;
this->name = name;
}
float getX() {return x;}
float getY() {return y;}
char *getName() {return name;}
void setX(float x) {this->x = x;}
void setY(float y) {this->y = y;}
void setName(char *name) {this->name = name;}
};بعد ذلك فإنه يأتي على "أخطاء التصميم الفادحة" التي ارتكبها هؤلاء المبرمجون....
نظرة واحدة على السطر التالي :
char *getName() {return name;}تخبرك بأن الـ Information Hiding تم انتهاكها, بإعادة مؤشر لبيانات تلك الفئة.
و الأفظع من ذلك عندما يكمل هؤلاء المبرمجون برنامجهم بكتابة مثل هذه الدالة :
NamedPoint makeCityCoordinate (float x, float y, char *cityName, char *countryName)
{
char temp[80];
sprintf (temp, "City: %s, Country: %s", cityName, countryName);
return NamedPoint (x, y, temp);
}و الخطأ الفظيع الذي حصل, أن الكائن الذي تم إرجاعه يحتوي على مؤشر يشير إلى المصفوفة temp و التي أصبحت في مهب الريح بعد الخروج من الدالة, بكل بساطة temp كانت على الـ Stack الخاصة بالبرنامج, و بعد الخروج من الدالة لم تعد ملكاً لبرنامجنا... و يعتبر هذا النوع من الأخطاء أحد أشهر أخطاء الذاكرة التي نسمع عنها في ++C/C, المشكلة أن هذا لنوع من المشاكل يجعل البرنامج unstable أي غير مستقر فهو يعمل حيناً و يتوقف حيناً آخر.. و لذلك من الصعب اكتشافه جداً إذا كان حجم البرنامج كبيراً نسبياً....
بالطبع يذكر مثال آخر عن الـ Heap Corruption .... :)
لن أطيل عليكم بالكلام.... و لكن باختصار هذا المبرمج هو مبرمج فشل في تعلم لغة C و تعلم JAVA و بعد تعلمه JAVA أصبح يعتقد أن مفهوم الـ Garbage Collection أو ما يسمى اختصاراً GC هو أحد أركان OOP و غير ذلك من المفاهيم الخاطئة التي يتعلمها المبرمج الحديث السن عندما يفكر في هندسة البرنامج أكثر من وظيفته الأصلية ... لا أدعي أن مبرمجي JAVA مبرمجون فاشلون على الإطلاق .. و لكن أقول أن من كانت أول لغة تعلمها هي JAVA و لم يتعلم إلى الآن أو لديه استيعاب جيد للغة C فلا يملك مقومات المبرمج الحقيقي :)
لست أدخل ++C في الموضوع, تعلم C و JAVA أو تعلم ++C و قم بهندسة برنامجك بالـ Methodology التي تحب :)
نعود لموضوعنا الأصلي ... لماذا كل هذا الكلام الخطير حول هذا المبرمج ... :)
إن أي شخص قرأ كتاباً على الأقل لـ ++C و تلاعبت أنامله بـ Visual CPP أو Borland CPP أو CB أو غيرهم يعرف أن أخطاء كاتب المقال هي من أساسيات ++C/C و اللغات التي تتعامل مع الذاكرة يدوياً.... و بما أن الكاتب تكلم عن البرمجة الكائنية بالتحديد... لذلك سيكون الرد مبنياً على ++C و لن نتكلم عن طريقة C "المثالية" في حل المثال ....
في ++C فإننا نملك مسارين للبرمجة ... الأول هي البرمجة الإجرائية و الثانية هي البرمجة الكائنية .... مبرمجو ++C يملكون أقوى عتاد على مستوى اللغات في مستوى التحكم في الـ Hierarchy... أهرام الكائنات... و ذلك نابع من امتداد فلسفة ++C بأن المبرمج هو الذي يجب أن يقوم بالعمل Dirty Job و اللغة لا توفر له إلا إمكانيات لتطبيق ما يريد....
في مثال الكاتب الأول فإنه عرف اسم "النقطة" في السطر التالي و لاحظ استخدام مؤشر من النوع char :
char *name;
دون إطالة في الكلام .. لديك في ++C كما في كل اللغات الأخرى ما يسمى String Class .... (أما النوع char فهو يستخدم على أنه low level arrays للتحكم على مستوى الـ byte) ,,,, تستطيع تعريفها كالتالي :
string pointName;
هذا الصنف مبني لكي يقوم بالتحكم الأوتوماتيكي بالذاكرة أي أنك لا يجب أن تقلق حول المؤشرات على الإطلاق!!!!
مالذي تريده أكثر من ذلك... هل سمعت عن شيء يسمى Static Polymorphism ؟!
تلك الفئة تمثل حروفها بترميز ASCII, ماذا لو أردت ترميز Unicode ؟!
كل ما عليك هو التالي :
wstring pointName;
و أصبح النص wide :)
و لكن في ++C هذا لا يكفي... هل لديك ترميز من القمر! لا مشكلة لديك الـ Template Class, المسماة basic_string هذا الصنف باختصار يعرف كل عمليات النص! و لكن نوع الحروف نفسها يمكن أن تحدده أنت :) هل لدى نصك مواصفات خاصة ؟ لا مشكلة, قم بتعريف traits مخصوصة لنصك, و لا تكتب سطر واحد من الكود في العمليات التي تتم على أي نص!
هل هذا يكفي ؟ توقف قليلاً و تذكر بأن الـ Performance لم تنقص ذرة منه في دعم كل هذه الميزات.. ببساطة كل العملية تتم في الـ Compile Time و ليس هناك parent و child لنوعين متشابهين من حيث العمليات مختلفين من حيث البيانات! ربما لا يعرف هذا المبرمج ماهي الفائدة الحقيقة للـ Dynamic Polymorphism :)
بالطبع يكمل... و يقول انظر إلى السطر التالي :
void setName(char *name) {this->name = name;}هل تلاحظ ؟ أصبح مؤشر الإسم الداخلي للكائن يؤشر على مصفوفة في مكان آخر في الذاكرة.. قد يحذفها المستدعي الأصلي للدالة setName !!!!
هذا من مبادئ التحكم في الذاكرة الذي لا يحتاج إلى ثلاث سنوات خبرة بعد التخرج! أي طالب درس C يعلم أن المصفوفة لم "تنسخ" و إنما ارسلنا مؤشراً لها, و الـ Argument الملازم لها لا بد أن يكون الطول :)
............................................................
و لكن هناك شيء مثير في المثال الثاني .... أود أن أعلق عليه .... قرأت في كتاب يتحدث عن لغات البرمجة أدرسه حالياً بأن JAVA أكثر مقروئية من ++C :)
أود أن أريك قطعتين من الكود و أريدك أن تحكم أيهما أسهل فهماً... على تصور أننا نريد نسخ الكائن address1 إلى الكائن address2 :
1) address2 = address1; 2) address2 = address1.clone();
أيهما أسهل فهماً الأولى أم الثانية... الأولى هي طريقة ++C و الثانية هي طريقة JAVA لأن JAVA لا تسمح بإعادة تعريف المعاملات Operators :)
++C هي ربما أحد أكبر اللغات من حيث الإمكانيات و اساليب البرمجة الممكنة و حتى مخترع اللغة نفسه لايدعي المعرفة الكاملة بلغته :) ++C ليست C و لكنها خليط من C و SIMULA67 و ALGOL68 و غيرها الكثير مما سمعت عنه و ما لم تسمع :)
و لو نظرت إلى كتاب الشهير Savitch حول JAVA ستجده يغطي اللغة و مكتباتها و معظم ما يتعلق بـ JAVA.... أو ما يسمى بـ Standard Edition ... بينما كتابه الشهير أيضاً حول ++C لا يغطي حتى أساسيات اللغة نفسها دعك عن مكتباتها رغم أن الكتابين في نفس الحجم تقريباً....
لغة ++C معقدة لأنها ليست لغة تتعلمها في يومين و تختبر بعض الأكواد في اليوم الثالث... هي لغتك في عالم البرمجة :) منذ أن تولد في هذا العالم إلى ماشاء الله... و لذلك نرى MS Office و Photoshop و ............
في النهاية أعرف أن الرد ملخبط و مقلوب رأساً على عقب لأني كتبته دون أي مراجعة .... :huh:
لذلك نقد الانتقاد مسموح :lol:
تحياتي ...


