الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

الرد على منتقد ++c!

بدأه Khaled.Alshaya في 5 مارس 2009 · 12 رد · 4,533 مشاهدة · في قسم المواضيع الهامة في قسم السي /سي++
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ,,

أشار الأخ الشمري في موضوع في القسم إلى مقال ينتقد ++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:

تحياتي ...

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 6 مارس 2009 في 00:17

#2

وعليكم السلام ,

- نقد موضوعي وفي مكانه ... فعلا .. الاخطاء هي بدائية .. ولا تتصور أن يقع بها شخص درس اللغة دراسة ( أنا لم أدرسها :D ) .

- بعد حصيلة مريرة من التجارب .. أجد أن عيب ++C هو بأنها تسمح لك بأن تفعل كل شيء .. كل شيء .. حتى الامور الغير مفيدة الا في حالة خاصة وخاصة جدا .. تسمح لك بها ؟ .. هذا يؤدي إلى تشتيت المبرمج , مثال :

في الجافا .. تعتبر أي بارمتر ( معامل ) في أي Method .. يتم تمريره بواسطه المرجع reference .. ( اذا كان Object ) .. بينما ++C , تسمح لك بأمور كثيرة :

Java  :

public Object func ( Object obj)

C++
Object  test( Object  obj);
Object  test( Object&  obj);
Object&  test( Object&  obj);
const Object   test( const Object  obj);
const Object&  test( const Object&  obj);

- وأعتقد أن الجافا .. سهلت على المبرمجين .. ولم تعقدهم كثيرا ..

- الأمر الاخر .. هو أحد عيوب مبرمجي السي بلس ( وأنا منهم ) , لا نستخدم المكتبة القياسية std , ثم نصيح عند وجود مشكلة في الذاكرة .. وهي النقطة التي أشار لها الاخ خالد .

- وأخيرا , الجافا تعتمد على مفاهيم OOP , وغيّرت العالم كلّه .. بأسلوبها الرائع في مكتباتها .. بالرغم من أن ++C تستطيع أن تقوم بما قامت به الجافا ( بالاعتماد على مبدأ Dynamic Polymorphism ) .. حتى لو خسرت 1 ميكرو ثانية .. من سرعة البرنامج .. أو خسرت 1 كيلو بايت .. من الذاكرة ؟

- ومع ذلك , أجد لذّة البرمجة بلغة ++C :D

- مرة أخرى , مشكلة السي بلس في ميزتها .. أنها تسمح لك بكل شيء ؟

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#3

وعليكــم السـلام ورحمة الله وبركاتـه..

نقد جيد أخي خالد ،، وصراحه المبرمج باين عليه وقتها كان غير ملم جيدا بمواضيع المؤشرات والcopy constructors و Assignment operator ، أو أن المبرمجين الذين تعاملوا معه يعانوا من نفس المشكله :) .

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

ناهيك عن وجود الكثير من الأمور في سي++ قد لا يحتاجها المبتدئ ولا ربما المحترف أيضا :) وخاصه في مواضيع القوالب ومتاهاتها ..

اقتباس
أود أن أريك قطعتين من الكود و أريدك أن تحكم أيهما أسهل فهماً... على تصور أننا نريد نسخ الكائن address1 إلى الكائن address2 :

1) address2 = address1;

2) address2 = address1.clone();

أيهما أسهل فهماً الأولى أم الثانية... الأولى هي طريقة ++C و الثانية هي طريقة JAVA لأن JAVA لا تسمح بإعادة تعريف المعاملات Operators

في حال عرفنا أن المبرمج لا يتعامل مع ال"كائن" مباشره في جافا بل من خلال مؤشر Rerefence to object ، فبالتالى تكون العباره الثانيه أسهل لأنها أكثر منطقيه :) .. هل تريد أن نستخدم العباره الأولى ونسند مؤشر لمؤشر !

لذلك لا يصح أن تقارن بكائن في سي++ مع مؤشر في جافا ، المقارنه تكون بين مؤشر ومؤشر ،، وننظر في الكود التالي من هو الأكثر مقروئيه :

Manager boss = (Manager) staff[1];  // Java
Manager* boss = dynamic_cast<Manager*>(staff[1]);	  // C++

:lol:

سي++ أصعب من جافا بكثير وعباراتها في كثير من الأحيان أكثر تعقيدا أيضا ، أنظر لSTL و Java Collections وشاهد سهوله وسلاسه عبارات مكتبه الجافا ، وحتى المعاملات في سي++ new,comma operator,using ,,etc ليست بهذه السهوله وكل منها قد يكون له استخدام ما في وقت ما خذ مثلا لدينا كلاس Super وأردنا أن نعيد تعريف Override أحد الدوال فيه في الأبن Sub ولكن مع تغيير الوسائط في الداله الجديده ،،

class Super {
	public:
		Super(){}
		virtual void someMethod(){ cout << "Super\n"; }
};

class Sub : public Super{
	public :
		Sub(){}
		virtual void someMethod(int x) { cout << "Sub x\n"; }
};

في الحاله اعلاه عمليه الoverride غير صحيحه حيث أخفت hidden الداله الجديده (بمعامل) الداله في الSuper،، وبالتالى لا يمكن أن نستدعي الداله بدون معامل .. الأن يمكنك أن تجد حل وهو استخدام الDefault Argument واعطاء الداله في Sub قيمه ما وبالتالى يكون حصلت عمليه الoverride بشكل صحيح .

class Sub : public Super{
	public :
		Sub(){}
		virtual void someMethod(int x=3) { cout << "Sub x\n"; }
};

الحل الأخر وهو الحل الغريب :) وهو استخدام المعامل using واستخدام الداله الموجوده في الأب كما هي بالضبط ..

class Sub : public Super{
	public :
		Sub(){}
		using Super::someMethod;		 // Explicitly “inherits” the Super version
		virtual void someMethod(int x) { cout << "Sub x\n"; }
};

اقتباس
لأن JAVA لا تسمح بإعادة تعريف المعاملات Operators

هي ميزه على كل حال :P . فجافا بدأت وكان أحد أهدافها تبسيط لمفاهيم سي++ المعقده بأسهل Syntax ممكن .. ونحجت في هذا ..

بعض الأحيان حتى تعريف المعاملات لن يكون بهذه السهوله خصوصا في حالات الوارثه أو استخدام الصداقه .. جرب أن تقوم بعمل داله freind لostream operator أو أي operator أخر في template class وانظر كيف سيكون شكل ال Syntax :)

اقتباس
ومع ذلك , أجد لذّة البرمجة بلغة ++C

:wub:

دمتم بخير ، وتشكر أخي خالد لطرحك الموضوع ... ونتمنى من أبو شمّر المزيد من أشباه الألغاز :P

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#4

الله يستر

ما فاهم اي شئ , مع العلم أنا مبتدأ ومحب لغة السي بلس بلس لدرجة الادمان

ادعوا لي بالتوفيق

بارك الله فيكم

#5

بالرغم من أني أعتبر الـ C++ أصـعب لغة برمجة على الإطلاق نظرا لتعقيدها إلا أني لم أتردد في تعلمها و فعلا هي معقدة جدا. لكن لا أدري لماذا أعتقد أن الجافا أصـعب علـما أني لم أتجرأ على تعلم الجافا لسبب لا أعلمه :(

بارك الله فيك أخوي على نقـدك الممـتاز!

شـبكة تقـنية أون لاين التعليـمية

دورات كـاملة مـجانية من الصفر حتى الاحتراف - كتب تعليمية - خـلفيـات و ملحقـات برامج = تصاميم جاهزة - خدمات مجانية - معا لتطوير المستخدم العربي

#6
اقتباس
- وأعتقد أن الجافا .. سهلت على المبرمجين .. ولم تعقدهم كثيرا ..

أتفق معك في هذه النقطة و أعتقد أنها سهلت زيادة عن اللزوم... :lol:

هل ترى في الحقيقة طرق تمرير البيانات بين الدوال هي إحدى "الميزات" التي تزعجني في Java, و إجبار المبرمج على طريقة واحدة هو خطأ تصميمي برأيي الشخصي :lol: أنا أقارنها مع ++C بالنسبة لي, و لست أعطي حكماً مطلقاً :)

كما قلت من يحتاج إلى كل تلك "الطرق" ؟!

في الحقيقة إن فهم الفرق بين تلك الطرق يسهل كتابة كثير من الكود بطرق متقدمة جداً نراها في المشاريع المنتشرة على الانترنت, فأنا لم أكتب برنامجاً حتى الآن يستخدمه غيري,

اقتباس
- وأخيرا , الجافا تعتمد على مفاهيم OOP , وغيّرت العالم كلّه .. بأسلوبها الرائع في مكتباتها .. بالرغم من أن ++C تستطيع أن تقوم بما قامت به الجافا ( بالاعتماد على مبدأ Dynamic Polymorphism ) .. حتى لو خسرت 1 ميكرو ثانية .. من سرعة البرنامج .. أو خسرت 1 كيلو بايت .. من الذاكرة ؟

في الحقيقة أن هذا الموضوع ربما يكون سبب عدم إيماني بـ Java على أنها لغة برمجة حقيقة, تصلح خارج الأسوار الأكاديمية :) إلا في حالات خاصة, بينما شعار الـ Java هو run everywhere,

و نراها فشلت في أحد أهم أشكال التطبيقات, تطبيقات سطح المكتب :)

كثير من الشركات الكبرى فشلت في نقل تطبيقاتها من ++C/C إلى Java.... ابحث حول هذه النقطة و ستجد الكثير!

لماذا البرمجة الكائنية فقط, و لماذا تحديد إمكانيات المبرمج حتى في أسلوب البرمجة الكائنية, ++C تدعم ثلاث أساليب للبرمجة و كل واحدة منها أعقد من الأخرى, Python أيضاً لا تتبع أسلوب معين, و الكثير الكثير من اللغات الأخرى,

"للأسف أن OOP أصبحت تعني عند الكثير من المبرمجين كود جيد"حسب ما يقول مخترع لغة ++C, و لهذا كتب ورقة من أجمل ما قرأت يشرح فيها لماذا ++C ليست مقتصرة على الكائنات أي أنها ليست Pure كما هي Java,

في المرفقات :)

أحد أشهر أسباب نجاح ++C و مجاراتها لـ C هو هدف وضعه لها مخترع اللغة سماه zero overhead principle أي أنك لا تدفع ثمن الـ performance لشيء لا تستخدمه :)

لم أنته بعد كانت تلك البداية,

لي عودة اليوم وغد بما أنني فاضي :lol: لكي أعرض حسب ما أفهمه فائدة كل طريقة من الطرق التي ذكرتها و متى أستخدمها, إضافة إلى إعلان الحرب على البرمجة الكائنية بشكل عام :lol: و خصوصاً على طريقة Java.....

الأخ وجدي, لا تذهب بعيداً أنت لك عودة مطولة :D

تحياتي ,,

oopsla.pdf

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 11 مارس 2009 في 06:12

#7
C++
Object  test( Object  obj);
Object  test( Object&  obj);
Object&  test( Object&  obj);
const Object   test( const Object  obj);
const Object&  test( const Object&  obj);

أخي الشمري,

حسب ما أفهمه رغمي ضعفي في الموضوع :) أن البرمجة الكائنية, في قلبها ليست إلا الـ imperative paradigm,

في اللغات الـ pure procedural, فإننا يمكننا تعريف عمليات, سواء كانت تلك العمليات فحص نص أو اقتطاع جزء من ذلك النص, أو أي عملية!

و لدينا مجموعة من الأنواع البسيطة, و عمليات معرفة عليها كـ built-in في اللغة نفسها,

و في تلك اللغات فإن أنواعك البسيطة يمكن تركيب أنواع جديدة منها, كما هو الحال مع struct في C,

الأنواع البسيطة يمكن أن تمررها للدوال بكل بساطة و تستعيدها منها, و لكن المشكلة تظهر عندما يكون ذلك النوع مركباً, و تحتاج تلك العمليات إلى عمليات قبلها و بعدها,

في الأنواع المركبة فأنت في الغالب تقوم بتغيير إحدى متغيرات "المتغير المركب" أو إعادة ناتج العملية بنوع آخر مغاير للمتغير المركب نفسه,

و بالضرورة فإنني سأتحكم في بناء تلك الـ Data Structure و متى أنشئها و متى أتخلص منها, و متى و متى :)

Windows API على سبيل المثال, ذهبت خطوة إلى الأمام في ذلك دون خسارة C Efficiency ... كل العمليات التي تتم على أي نوع مركب, تتضمن عمليات التحكم به :) و لذلك المبرمج لا يقلق سوى حول الدوال التي يتعامل معها و ليس حول تمثيل الـ Data Structure نفسها...

هنا البرمجة الكائنية توفر لنا حلاً من شدة فائدته أن معظم البرمجيات منذ بداية التسعينات أصبحت تكتب بهذا المفهوم.

البرمجة الكائنية قديمة قدم البرمجة الإجرائية :) منذ Simula67 حيث اقتبست ++C تلك الأفكار منها, و نشرتها للعالم, بأفضل لغة procedural في ذلك الزمان C :)

و ++C سمحت لك بأن تعرف "بالضبط" كيف سيكون الحال عندما يكون لدينا أنواع جديدة معرفة "البيانات مع العمليات", و لهذا لدينا Constructor و Destructor لأننا نحتاج 99% من الأحيان إلى عمل initialization و finalization ...

و خطوة أخرى, سمحت لك بأن تعرف Operators على تلك الأنواع الجديدة :)

هناك بعض الأنواع الجديدة بطبعها لايمكن أن تشتق منها نوعاً كحالة خاصة من الكائن الأب :) خذ مثلاً الكائن string ماذا يمكن أن تشتق منه ؟!

أو vector أو حتى لو كان لديك صنف جديد يسمى MAPM يمثل arbitrary precision number, هذه ليست مفاهيم مجردة كـ car أو employee :)

هذه أنواع جديدة فقط لا غير, لكي أقوم بعمليات أريدها على "أعداد" من النوع MAPM فكل ما يلزمني كتابة التالي :

MAPM anyNumber = 333666;

for( MAPM i = 2; i <= 333666; i++ )
	anyNumber *= i;

حيث i و anyNumber هما نوع جديد كلياً و لكنهم أعداد في النهاية, إعادة تعريف المعاملات ليست لكل شيء و لكن عندما يكون لدينا شيء من البديهي أن ما يحصل بين تلك الكائنات هو عبارة عن "عمليات" يمكن تمثيلها بتلك الرموز في الأساس:)

و لذلك ++C لا تسمح لك بتعريف معاملات جديدة كما في لغات أخرى...

أعرف أنني لم أجب على مثالك حتى الآن, و لنأخذ مثالي على أنه الحل :)

لو أردنا إعادة تعريف العملية التالية في الكود السابق :

MAPM anyNumber = 333666;

ما هي الطريقة الصحيحة لإعادة تعريف المعامل '=' ؟!

ما رأيك بالسطر التالي :

MAPM &operator=(const MAPM &m)

في الحقيقة أن هناك مشكلة "صغيرونة" في هذا التعريف, و هذا ما وقع به كاتب الـ class لهذه المكتبة الجميلة,

مبرمج المكتبة نفسها مبرمج C كبير بمعنى الكلمة, المكتبة نفسها مستقرة جداً إذا كنت تستخدم واجهة الـ C و تقوم لغة Lua الشهيرة باستخدام المكتبة, و لكن الصنف كتبه مبرمج آخر, و تبرع به,

و يبدو أنه على حالاتنا في أول الطريق :) جهد رائع منه في الحقيقة...

ماذا لو قمنا بالتالي هل هو صحيح :lol: :

MAPM anyNumber1 = 3, anyNumber2 = 5, anyNumber3 = 7;
(anyNumber1 = anyNumber2) = anyNumber3;

هل هو صحيح و لماذا :)

إذا لم أستطع الحل فليست المشكلة من اللغة نفسها و لكنها مني أنا, و السبب أن الـ Expression في ++C يمكن أن تكون Statements حتى لو كانت الأنواع في تلك الـ Statements هي كائنات :)

فالمثال الذي في الأعلى يمكن أن يصحح للتالي :

const MAPM &operator=(const MAPM &m)

و بهذا يصدر المترجم خطأ يخبرنا فيه أن العبارة

 (anyNumber1 = anyNumber2)

بعد حلها ستعيد لنا

const MAPM&

و بالتالي فإن الثابت لايمكن تغييره و تصبح العبارة خاطئة لأنها في الأساس ليست صحيحة أو ذات معنى :)

هل ترى درجة التحكم, مستخدمو الكائنات في الحقيقة يرتاحون كثيراً جداً, و انظر إلى التصميم الرائع لـ qt و سترى فنون التصميم, هذا يعني أن صنفنا يجب أن يكون usable قبل أن يكون reusable :)

اقتباس
- وأخيرا , الجافا تعتمد على مفاهيم OOP , وغيّرت العالم كلّه .. بأسلوبها الرائع في مكتباتها .. بالرغم من أن ++C تستطيع أن تقوم بما قامت به الجافا ( بالاعتماد على مبدأ Dynamic Polymorphism ) .. حتى لو خسرت 1 ميكرو ثانية .. من سرعة البرنامج .. أو خسرت 1 كيلو بايت .. من الذاكرة ؟

هذا ما يجعل معظم البرامج في "العالم الحقيقي" :lol: مبنية بـ ++C,

هذه المايكروثانية, لا تغتفر في البرامج الحقيقة كبرامج الرسم و الصوت و العلمية و الألعاب كـ مثال حقيقي جداً :)

و لكن انتظر لماذا تريدني أني أفقد تلك المايكرثانية.... أتمنى أن تكون لسبب حقيقي,

الآن دعك من الـ operators overloading و ألقي مثال على equals في java,

لماذا هي overridden من كائن يسمى Object و لماذا علي التحقق من نوع الكائن إذا كان من نفس نوع الكائن المقارن ؟

هذه العملية أصلاً يمكن التحقق منها وقت الـ Compilation :)

إذا قمنا ببناء شجرة من الكائنات فإننا عندها في الغالب سنحتاج إلى dynamic binding .... في Java كل الدوال هي دوال يتم ربطها وقت التشغيل ؟! لماذا ؟

و لماذا كل الأصناف الجديدة لدي ترث من كائن اسمه Object في الأساس!

ما الداعي الحقيقي لهذا الكائن ؟!

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

كمثال رائع على شجرة أصناف انظر على مكتبة الـ io stream في ++C, حيث بتعلمك لكيفية استخدام الـ ostream و istream تستطيع بعدها فعل العجائب, البعض يعتقد أن الكائنان cout و cin هما المكتبة :)

في الحقيقة ما هما إلا كائنان معرفان من المكتبة و يستفيدان من خدماتها التي لا تحصى, هل جربت التعامل مع الكائن stringstream, كائن رائع بكل معنى الكلمة للتعامل مع سلاسل البيانات الممثلة على شكل نصوص, و الأفضل من ذلك أن واجهته لا تختلف إطلاقاً عن istream و ostream

إذا كنت تعرفهما فأنت تعرف stringstream

و لكن في حالة vector فإننا عندما نريده أن يكون generic نضع أباً افتراضياً Object و نمرر تلك الكائنات له على هذا الأساس ؟! هل هذا هو الحل..

الحل كما يقوله العالم الشهير Alexander Stepanov بأن يكون الحل وقت الترجمة و ليس وقت التشغيل :)

أخ وجدي انتظر ردي :P

تحياتي ,,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 12 مارس 2009 في 09:15

#8

أنت شكلك صدقت ، أنا كنت بمزح :P

كلامك جميل جدا ، لكن هناك نقطه بسيطه وهي أن الجمله :

MAPM anyNumber = 333666;

لا تسبب في استدعاء الAssignment oprerator ، بل داله البناء العاديه ، لذلك الجمل :

MAPM anyNumber1 = 3, anyNumber2 = 5, anyNumber3 = 7;
(anyNumber1 = anyNumber2) = anyNumber3;

جمل طبيعيه ولا غبار عليها .. ووضعك للconst في داله = سوف يقيدنا باسناد واحد فقط ولن يمكننا من اجراء اسناد متداخل مثل :

object1 = object2 = object3

اقتباس
قد لا يوجد من يقوم بهكذا اسناد ، لكن لما نقيد مستخدم الكلاس بأمر لا يوجد منه ضرر ..

و لماذا كل الأصناف الجديدة لدي ترث من كائن اسمه Object في الأساس!

ما الداعي الحقيقي لهذا الكائن ؟!

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

في جافا الكلاس Object يمثل مجموعه من العمليات ضروريه لأي كائن ، فهل يمكن أن يكون هناك كائن لا نستطيع مقارنته مع كائن أخر ؟ لذلك توجد داله compare .. هل يوجد كائن لا نستطيع طباعه بياناته ؟ لذلك توجد داله toString وهكذا .. حتى qt نفسها تستخدم نفس الأسلوب بالضبط .. وهو أسلوب منطقي تماما فالوراثه هنا لأجل اضافه المزيد من الوظائف ..

لدى اختبار بعد أقل من 10 دقائق :( .. دعواتكم لي بالتوفيق ..

أخي الشمري لا تنسى "الفزعه" ، فافزعلي بعدين زي ما فزعلتك الحين :lol:

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#9

أهلاً أخي وجدي :)

الـ Copy Constructor ليس chainable كما هو الحال في الـ assignment ... أنا كلامي على هذا السطر

(anyNumber1 = anyNumber2) = anyNumber3;

إذا لم تكن القيمة المعادة const فالـ statement التي في الأعلى صحيحاً تماماً و لا غبار عليها :)

و لكنها ليست ذات معنى أساساً, لذلك نحن نتحكم في كيفية كتابة الـ statements و الـ expressions عندما نقوم بإعادة تعريف معامل أو تعريف دالة جديدة مهما كانت :)

اقتباس
ووضعك للconst في داله = سوف يقيدنا باسناد واحد فقط ولن يمكننا من اجراء اسناد متداخل مثل :
اقتباس
object1 = object2 = object3

لو كانت القيمة المعادة const أو لم تكن const فإن الـ statement التي في السطر السابق صحيحة .... المشكلة تظهر عندما يتلاعب المبرمج بعبارات الإسناد, لأنها كما تلاحظ تتم عملية الـ evaluation من اليمين لليسار عكس جميع الـ operators في جميع لغات البرمجة :)

اقتباس
في جافا الكلاس Object يمثل مجموعه من العمليات ضروريه لأي كائن ، فهل يمكن أن يكون هناك كائن لا نستطيع مقارنته مع كائن أخر ؟ لذلك توجد داله compare .. هل يوجد كائن لا نستطيع طباعه بياناته ؟ لذلك توجد داله toString وهكذا .. حتى qt نفسها تستخدم نفس الأسلوب بالضبط .. وهو أسلوب منطقي تماما فالوراثه هنا لأجل اضافه المزيد من الوظائف ..

سؤال طيب ... لنفترض أن لدي صنف يسمى bmp يمثل صورة, و لدي صنف آخر يسمى textbox :)

ما الفائدة من المقارنة, أو التحقق من المساواة ؟

يمكنني اكتشاف أن هذين الكائنين ليسا متساويين وقت ترجمة الكود! و ليس بعد تشغيله و التحقق أنهما ليسا من النوع نفسه :) و هذا ما تقوم به ++C.

ماذا يمكن أن تكون toString لكائن من النوع bmp ؟!

بالنسبة لـ Qt فهو framework, و لا مشكلة في ذلك كما قلت في السابق, مشكلتي ليست في الوارثة فهي رائعة و لكن أن تلتقي كل تلك الأشجار في النهاية فهذا ما لم أستوعبه :)

برأيي أن Java لغة perfect أكثر من اللازم ... و لذلك يحبها دكاترة الجامعات.... و أعود و أقول بأن من تعلم في البداية بـ Java و لم تلامس يداه لغة C, فهو يقوم بكتابة كود لا يعرف ماذا يفعل هذا الكود بعد ترجمته!

بالتوفيق أخ وجدي :wink:

تحياتي ,,

#10

للامانه واحقاقا للحق ماشاء الله عليك

المصبيه

لو اتى المبرمج بحيلة في الإسناد اعتقد انه راح بيدمر كل العمليات واتصور هذه مشكله

لكم مني خالص التقدير

*********************

دروس الاكسس

قاعدة بيانات بالسي++

قاعدة بيانات اخرى بالسي++

الفريق العربي للبرمجه

*********************

كان الله في عون العبد مادام العبد في عون اخيه

#11

علما اني جلست افكر في الباني راحي يعمل مع تعريف الاوبجيكت تلقائيا لهذااعتقد ممكن فيه حيله من الداله بالاساس

لكم مني خالص التقدير

*********************

دروس الاكسس

قاعدة بيانات بالسي++

قاعدة بيانات اخرى بالسي++

الفريق العربي للبرمجه

*********************

كان الله في عون العبد مادام العبد في عون اخيه

#12
اقتباس
أخي الشمري لا تنسى "الفزعه" ، فافزعلي بعدين زي ما فزعلتك الحين

هذا ما سأقوم به الان .. انظر فقط :D

كلامك مقنع أخي خالد , ( في بعض النقاط ) , مثلا :

اقتباس
و نراها فشلت في أحد أهم أشكال التطبيقات, تطبيقات سطح المكتب
اقتباس
++C تدعم ثلاث أساليب للبرمجة و كل واحدة منها أعقد من الأخرى

وقد أوافقك على أن المكتبة القياسية للغة السي بلس , من المهم أن لا تعتمد على dynamic polymorphism , لانها الاساس للكثير من البرامج .

لكن سأخالفك بنقطتين :

اقتباس
هذه المايكروثانية, لا تغتفر في البرامج الحقيقة كبرامج الرسم و الصوت و العلمية و الألعاب كـ مثال حقيقي جداً

- الان أغلب محركات الألعاب والألعاب نفسها تعتمد كثيرا على البرمجة الكائنية ( وتحديدا على مفهوم dynamic polymorphism ) , مثال واضح :

- محرك Ogre المفتوح المصدر .

- محرك irrlicht المفتوح المصدر .

انظر لطريقة تصميم المحركات .. ستجد أنها كائنية وتعتمد أقصى درجات مفاهيم OOP , بما فيها الاعتماد الكبير على virtual methods .

لأنك لاتتصوّر , جهاز يقوم بتشغيل لعبة على مستوى عالي , لايكون قادر على استيعاب متطلبات الـ dynamic polymorphism .

90% من برمجيات السي بلس المهمة .. التي أراها الان صارت تقوم بما تقوم به الجافا ..

- إن أحد أسباب ضعف الجافا في برمجيات الالعاب وتطبيقات سطح المكتب ليس البرمجة الكائنية التي تنتهجها .. ولكن اعتمادها على JVM كاطار عمل لتنفيذ Java bytecode .. واعتمادها على الGarbage collection , هو الذي يسبب بذلك البطء .. أليس كذلك :) ؟

اقتباس
ماذا يمكن أن تكون toString لكائن من النوع bmp ؟!

نستفيد منها في تتبع البرنامج( الكثير من الـ method , تفرض أنك تملك مثل هذه الدالة , مثلا System.out.print ) . . لنطبع مثلا طول وعرض واسم ونوع الصورة .. افعل بها ما تشاء .

اقتباس
برأيي أن Java لغة perfect أكثر من اللازم ... و لذلك يحبها دكاترة الجامعات.... و أعود و أقول بأن من تعلم في البداية بـ Java و لم تلامس يداه لغة C, فهو يقوم بكتابة كود لا يعرف ماذا يفعل هذا الكود بعد ترجمته!

كلام سليم جدا ..

لكن في الجهة المقابلة .. من اكتفى بلغة ++C فلن يكتب كود جميل OOP .. مثل الموجود بالجافا :) .

مرة أخرى ... قلبي يتسع لمحبة الجافا و ++C :D ..

تم تعديل هذه المشاركة بواسطة الشمري في 19 مارس 2009 في 11:22

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#13

اعتقد ايضا ان احد اسباب قوة السي++ انها تسمح بال low-level access لعتاد الكومبيوتر علشان كدا غالبية طلبة الكليات العلمية (اللي عنده صبر وشجاعة وفضول ) بيتعلم السي++ لانه بيحب يعرف كل شئ عن الكمبيوتر (زاكرة -معالج -هارد )

وبعدين الجافا جات لتبسط السي++ صحيح هي منظمة جدا وينضم ليها ايضا ال السي شارب ولكن رأيي انها ليس لغات برمجة هي مجرد مكتبات منظمة لا اكثر

وبعدين زي ما بيقولو If you know C++ you know every thing

مواضيع مشابهة