Khaled Alshaya

(متميزة)إحدى المشاكل المخادعة في فئات ++c (ترتيب تعريف المتغيرات)

17 ردود في هذا الموضوع

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

عندما بدأت البرمجة بلغة ++C, لم أعرف أنه كان ينتظرني الكثير في عالم الفئات !

عندما أقول الكثير فأنا أعني ذلك بكل ما تحمله الكلمة من معنى :rolleyes:

في السطور التالية, ستجد إحدى المشاكل التي قد يشيب الرأس لها و لا تستطيع اكتشافها :D , هذه المشكلة وقعت لي عندما درست مادة الـ++C, حتى أكون معكم صريحاً حتى الشخص الذي كان يقيم لي برنامجي لم يستطع اكتشاف العلة :D

المهم أني قرأت و قرأت و قرأت و لم أجد حلاً لمشكلتي, إلى أن ألهمني الله الصبر مع الأستاذة ++C :D وو فقني لاقتناء مرجع محترم لها, حينها عرفت مشكلتي,

سأطرح لكم المشكلة و سأنتظر رأيكم في المشكلة لنصل إلى الحل,

تصور أن لديك فئة بسيطة جداً :

class Test{
public :
Test(): y(3), x(y) {};

void printXY(){
cout << "x = " << x << endl << "y = " << y;
};
private :
int x;
int y;
};

المهم أني في الـ constructor الخاص بالفئة قمت بعملية الـ initialization للمتغيرات الخاصة بها, و هما x و y,

طبعاً فمت بتعريف الفئة كالتالي في برنامجي :

	Test problem;

و أسميت الكائن "مشكلة" problem :D ,,

كل ما سيحدث أن y ستحمل الرقم 3 بعد ذلك سيتم مساواة x لتصيح مساوية لـ y ,

y = 3
x = y // x = 3

بسيط جداً أليس كذلك :clapping: ,

الآن قم باستدعاء الدالة printXY من الكائن في برنامجك :

	problem.printXY();

ستظهر لك المشكلة العويصة , قيمة x ستكون عشوائية أما y فستساوي 3 كما شرحنا سابقاً ,

السؤال هنا, لماذا x يحمل قيم عشوائية, ألم نساويه بـy التي تساوي 3 أصلاً ؟؟ :resentful:

أنا في انتظاركم ,,

تحياتي ,,

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أخى أتذكر أن ترتيب العمليات فى ال Initializer list غير معرف فى ال standard و بالتالى ﻻ يمكن التنبؤ به, والله أعلم.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم:

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

كتبت عده متغيرات مختلفه بنفس الطريقه بعضها ضبط والاخر لا.

لكن في الاخير عرفت اصححها.

فقط كتبت:


private :
int y;
int x;

غيرت الترتيب لكن ياليت نعرف المصرف كيف ينفذ العمليه؟

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بعد تجربة إتضح إن ترتيب التعريف فى الكلاس هو اللى بيأثر فى ترتيب ال initialization يعنى

int x, y;

ال x يحصلها initialization قبل ال y

int y, x;

العكس

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

الذي اتضح لي بعد عدة تجارب أن الـ compiler يقوم بحجز مكان في الذاكرة للمتغيرين أولا ثم يقوم بعمل الـ initialization على حسب ترتيب المتغيرات في private

يعني سيقوم بحجز مكان لـ x ثم y ثم يقوم بعمل initialization لـ x وفي هذه الحالة ستكون القيمة المخزنة بـ y قيمة عشوائية ثم سيقوم بعمل initialization لـ y

والله أعلم

0

شارك هذا الرد


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

int x, y;

ال x يحصلها initialization قبل ال y

int y, x;

العكس

إذا كان هذا هو الذي يحدث بالضبط فهذا يعني أن الـ compiler سيعطي رسالة error عند تعريف x لأنه سيفاجأ أنها معتمدة على y وهو لم يتعرف على y بعد بمعنى أنه سيعطيك رسالة y : undeclared identifier

لذلك قلت أنه يقوم بحجز مكان لهما أولا ثم يقوم بعمل الـ initialization بعد ذلك

أنا عموما لست متأكد من صحة كلامي هذا مجرد تخمين

تم تعديل بواسطه باسم الموجي
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

ماشاء الله يا أخوة, لو أني طرحت السؤال يومها في المنتدى ما كان شاب شعر رأسي :D

المهم أن كلامكم صحيح 100%, عملية الـ initialization تتم حسب ترتيب المتغيرات في الفئة و بغض النظر عن ترتيب الـ initialization list التي تأتي بعد الـ constructor :)

بكل تأكيد يمكنكم تلافي المشكلة بتعريف الـ constructor الخاص بالفئة test كالتالي :

Test(){
y = 3;
x = y;
}

طبعاً هذا حل و لكنه ليس عملياً في جميع الحالات, سأحاول أن ألقي نظرة على الفرق بين الطريقتين و فائدة كل طريقة, و لكن بعد فاصل قصير :)

تحياتي ,,

تم تعديل بواسطه Khaled.Alshaya
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم من جديد ,,

الحقيقة أن ++C تملك الكثير من المميزات "الكثيرة الدقيقة" التي تمكننا من وضع الحلول الأفضل لما نريد فعله, و لكن كثرة التفاصيل تسبب الكثير من التشويش لدى المبرمجين المحترفين قبل المبتدئين :happy:

إحدى هذه المشاكل هي التفريق بين عملية الـ initialization و عملية الـ assignment..

حتى أبدأ في الموضوع لنطرح مثال بسيط, تصور أن لدينا السطر التالي في برنامجنا, و كل ما نريده أن تصبح x تساوي 3:

int x = 3;

بالمناسبة يمكن كتابة السطر الأول بطريقة أخرى و لكن الطريقتين متطابقتين تماماً :

int x(3);

أو يمكننا تطبيق نفس الشيء بطريقة أخرى :

int x;
x = 3;

الطريقتان السابقتان تقومان بنفس المهمة, و لكن هل يخطر على بالك أن بينها فرق جوهري :rolleyes:

في الحالة الأولى x ستصبح قيمتها تساوي 3 عند حجز الذاكرة الخاصة بها مباشرة! أما في الحالة الثانية فسيتم حجز الذاكرة ثم سيتم مساواة x بـ 3 !

بالنسبة للأنواع البسيطة (الأولية) في ++C استخدام أي طريقة قد لا يؤثر(لم أصادف حالة واحدة لا يمكن أن أستخدم إحدى الطريقتين فيها دون إمكانية استخدام الأخرى).

أما عند الحديث عن الفئات فالوضع مختلف تماماً, و إن كنت لا تعرف الفرق فأنت لا تعرف ماهي الفئات حقاً في ++C :(

و لكن لا مشكلة فلا يولد الشخص متعلماً :D

المهم, كل حديثنا يتركز على الفرق عند تعريف الـ constructor الخاص بأي فئة نريد تصميمها,

الفئة Test بسيطة إلى أبعد الحدود و لامشكلة في استخدام إحدى الطريقتين لأن الأنواع المعرفة في الفئة بسيطة كما قلت سابقاً,

كما ذكرت في السؤال الذي في المشاركة الأولى قد تقع في الخطأ الذي وقعت فيه أنا أصلاً! و لكن القصة تبدأ من هنا.

إذا كان لديك السطر التالي في الفئة test :

	Test(): y(3), x(y) {};

فالمتغيرات ستحصل على القيم لحظة حجز الذاكرة الخاصة بها و تمسى هذه الطريقة بالـ Initialization list, أما إذا كتبنا التالي :

Test(){
y = 3;
x = y;
}

فسيتم حجز الذاكرة, ثم سيتم إسناد القيم, أي أن عملية الـ initialization تمت للمتغيرات المعرفة ثم تم استخدام المعامل = لإسناد القيم.

و لكن لماذا كل هذا الاهتمام بالفرق بين الطريقتين إذا كان يمكننا استخدام إحداها بدل الأخرى في الفئات :hmm:, الكلام السابق ليس صحيحاً بشكل كامل! لثلاثة أسباب بشكل بشكل رئيسي :

1- أن الطريقة الأولى هي الطريقة الصحيحة :D

2- لأن أداء برنامجنا سينخفض عند إذا استخدمنا الطريقة الثانية !!

3- هناك حالات سنستعرض بعضها سوية, لا يمكن استخدام الطريقة الثانية معها !!

السبب الأول, لأننا نريد عمل initialization و ليس مساواة assignment !

بالنسبة للسبب الثاني, فهو ليس واضحاً بشكل كبير و لكن تصور أن لدينا الفئة التالية :

class Test{
string x;
object y;
};

تحتوي على نص من نوع string و كائن من نوع object (للتوضيح فقط), و لنفرض أننا استخدمنا الطريقة التالية في الـ constructor :

Test( string n, object o){
x = n;
y = o;
}

في هذه الحالة سيظهر فرق الأداء, أي عندما تحتوي فئتنا على فئات أخرى! فكائن النص سيقوم بعملية الـ initialization بشكل عادي (تم استدعاء الـ default constructor ) ثم سيتم مساواته بالنص n, بالطبع هاتان العمليتان تتخللهما عمليات حجز ذاكرة و تغيير قيمة متغيرات! أي أن العملية تمت على مرحلتين أو بشكل دقيق تم استدعاء دالتين!

أما لو أننا استخدمنا الطريقة التالية :

Test( string n, object o): x(n), y(o){};

فإن ما سيتم هو استدعاء الـ initializer الخاص بكائن النص أو ما يسمى بالـ copy constructor!

بالطبع لم أذكر الكائن object لسبب بسيط و هو أنه قد تحصل لدينا مشكلة معه إذا ساويناه كالتالي :

y = o;

لماذا ؟؟

لأن إعادة تعريف المعامل = ليس شرطاً متعارفاً عليه عند تصميم الفئات, على عكس الـ copy construcotr الذي يعتبر من الأمور المتعارف عليها(شبه قياسية) عند تصميم أي فئة!

السبب الثالث يظهر جلياً لو أننا نظرنا إلى الفئة التالية :

class Test{
const int x;
};

طبعاً ستضحكك علي و تقول هل أنت واثق من كلامك :haha: ! x يجب أن تكون static و لا يمكن تعريف الثوابت داخل الكائنات إنما تكون static مشتركة بين جميع الكائنات !

ألم أقل لك أنك إن لم تعرف الفرق بين الطريقتين اللتين نتكلم عنهما فأنت يفوتك الكثير!

++C لا تسمح بعملية إسناد قيم للثوابت في الـ constructor الخاص بالفئة و لكنها تسمح بعملية initialization للثوابت!

Test(): x(5){};

إلى هنا أعتقد أني تعبت من الكتابة, و لكن حتى لا تتوقف هنا, هناك الكثير من الأمور المتقدمة التي لم أشر إليها, كالفرق بين استخدام الثوابت الـ static و الثوابت التي ذكرناها و الحالة الوحيدة الباقية التي يجب استخدام طريقة الـ initialization list معها هي عندما نعرف متغير بالمرجع و لكن هذا المفهوم متقدم قليلاً و إذا أردتم مناقشته فلا مانع لدي خصوصاً أنه يريحنا من عناء المؤشرات!

إذا كان هناك سؤال أو توضيح سأجيب عليه بإذن الله,,

تحياتي ,,

تم تعديل بواسطه Khaled.Alshaya
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم اخوان ..

نفس كل مره ..

الاخوة كفوا ووفوا وما تركوا مجال للاجابه ..

اخي خالد .. المشكله كما ذكر الاخوان وباختصار هي فقط تسلسل اعطاء القيم للمتغيرات ..

والحل هو كما تفضلت بالشكل التالي .

Test(){
y = 3;
x = y;

}لان هذا التعريف يمكن ان يكون مفصلا ومفهوما اكثر بالنسبه للكومبايلر من التعريف السابق .. ..

كذلك اذا اردنا ان يكون التعريف اكثر متانه يمكن ان يكون بالشكل التالي ..

Test(){
y = x = 3;
}

تحياتي العطرة ...

تم تعديل بواسطه سنان محمد صالح
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

اخي خالد .. المشكله كما ذكر الاخوان وباختصار هي فقط تسلسل اعطاء القيم للمتغيرات ..

والحل هو كما تفضلت بالشكل التالي .

Test(){
y = 3;
x = y;
}

لان هذا التعريف يمكن ان يكون مفصلا ومفهوما اكثر بالنسبه للكومبايلر من التعريف السابق .. ..

كذلك اذا اردنا ان يكون التعريف اكثر متانه يمكن ان يكون بالشكل التالي ..

Test(){
y = x = 3;

}

معذرة أخ سنان يبدو أنك لم تقرأ مشاركتي الأخيرة, يمكنك قراءتها لمعرفة الفرق بين الطريقتين, الطريقة التي ذكرتها أنت غير صحيحة,

إذا كان لديك سؤال أنا في الخدمة.

تحياتي ,,

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

اعتذر اخي خالد ..

لقد حدثت مشاكل اثناء القيام بعملية الرد ..

وبعد ان وضعت الرد وجدت مشاركتك ..

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

لكن بعد فاصل قصير

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

تحياتي العطرة ..

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم

فعلا النقطة دى من اكتر المواضيع غموضا فى ال C++

لكن بمناسبة الموضوع ده , عندى كود غريب جدا ومخادع جدا ايضا , هاكتبه و قولولى الكود ده بيعمل ايه (من غير ما تجربوه طبعا :lol: )

//array of integers
int *arr;
arr=new int[10];

//function return (By Reference) with specific element in arry
int & myfunc(int x)
{
return arr[x];
}

//call the function in main

int main()
{
myfunc(3)=9;
return 0;
}

طبعا كل الخدعة فى هذا السطر

myfunc(3)=9;

ان السطر ده بيعمل Assign لل Function بقيمة معينة , وليس العكس كالمعتاد طبعا

مع العلم ان هذا السطر لا يصح ابدا الا اذا كانت ال Function بترجع قيمة By Reference !!!

تم تعديل بواسطه hanysaad
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

أخبارك يا هاني :)

انت جبت السؤال منين :wacko:

بس أعتقد أن العنصر الرابع في المصفوفة راح يساوي 9,,

لأن الدالة اللي عندنا بترجع مرجع للعنصر رقم x في المصفوفة,,

تحياتي ,,

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم

أخبارك يا هاني

انت جبت السؤال منين

ازيك يا خالد :) ؟

الموضوع ده خدناه لما كنا بنعمل String Class , وكنا بنعمل Operator Overload لل [ ] , عشان يستبدل Charater معينة فى ال String ب Character اخرى

بس أعتقد أن العنصر الرابع في المصفوفة راح يساوي 9,,

لأن الدالة اللي عندنا بترجع مرجع للعنصر رقم x في المصفوفة,,

بالضبط كدا يا خالد :thumb_up:

الطريقة دى بتنفع لما تكون ال Function بترجع Reference لاى متغير مثلا , فبتخلى قيمة هذا المتغير تساوى القيمة اللى بنساوى بيها ال Function

بس بصراحة الحتة دى عجبتنى قوى فى ال C++ B) ...

تم تعديل بواسطه hanysaad
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
ازيك يا خالد ؟

الموضوع ده خدناه لما كنا بنعمل String Class , وكنا بنعمل Operator Overload لل [ ] , عشان يستبدل Charater معينة فى ال String ب Character اخرى

انت طلعت سي بلساوي مخضرم و انا مو عارف :lol:

الطريقة دى بتنفع لما تكون ال Function بترجع Reference لاى متغير مثلا , فبتخلى قيمة هذا المتغير تساوى القيمة اللى بنساوى بيها ال Function

بإذن الله موضوعي الجاي راح يكون عن المتغيرات المرجعية, رغم ان الناس تفتكر إن المتغيرات في ++C إما عادية أو مؤشرات بس في نوع ثالث ما بتتكلم عنه الكتب غالباً,

و راح نحطه تحت المجهر B)

تحياتي ,,

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
انت طلعت سي بلساوي مخضرم و انا مو عارف

لا , انا دوت نتاوى ابا عن جد :lol:

بإذن الله موضوعي الجاي راح يكون عن المتغيرات المرجعية, رغم ان الناس تفتكر إن المتغيرات في ++C إما عادية أو مؤشرات بس في نوع ثالث ما بتتكلم عنه الكتب غالباً,

و راح نحطه تحت المجهر

موضوع مهم فعلا , فى انتظار الدرس ان شاء الله ,,,
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
زوار
This topic is now closed to further replies.

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .