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

متى نرسل الكائن أو نرجعه من أحد الدوال By Reference أو By Value ؟

مغلق
بدأه Khaled.Alshaya في 6 ديسمبر 2007 · 16 رد · 4,456 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع
اقتباس
متى نرسل الكائن أو نرجعه من أحد الدوال by reference أو by value ؟

هذا السؤال يواجهنا عندما ننتهي من دراسة الكائنات و Operators Overloading و الـ Friend Functions ,,,, المهم هي مفاهيم بسيطة نستفيد منها في البرمجة الكائنية بشكل كبير !

بالنسبة لإرسال الكائن بالقيمة by value فهذا يكون من دالة داخل كائن member function أو خارجها و مثال على ذلك هو لو كان لدينا مصفوفة و أردنا البحث عن كائن فيها فيتم ارجاع الكائن by value ,,

بالنسبة للإعادة by reference فيمكن أن يصلح المثال الذي في الأعلى كما في STL Vector و غيرها من الـ Containers و هي بكل بساطة عن أوعية لحفظ البيانات ,,

و لكنها اشتهرت في عملية الـ Operators Overloading لأنها تمكننا من انجاز شيء قد يبدو صعباُ في البداية و لكنه من أمتع أنواع البرمجة,, لأنه يمكننا من استعمال الـ Operators مع الفئات لإنجاز أشياء لم تخطر في البال !!!!

هل جربت استخدام string في ++C ؟

string str1, str2, str3;
str1 = "Operators OverLoading";

str3 = str2 = str1; //  = all of them = "Operators OverLoading"

لو لاحظت العملية التي ساوينا فيها str1 عملية إعادة تعريف = Operator لا غير و لو قمنا بكتابة الكود لكان داخل الكائن كالتالي :

void operator =( string &NewStr );

مع ملاحظة أن المعامل يعيد void أي أنه لا يعيد شيء!!!

و لو أننا أردنا معرفة بعض المعلومات التقنية عن معنى void فهي بكل بساطة ان الشيء المعاد من الدالة ليس معرفاً,, بدينا نتكلم في الرياضيات :D

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

و الآن لننظر إلى عملية المساواة الثانية ,, بالطبع تعرف أن عملية المساواة ستتم بين str1 و str2 أولاُ حسب عملية الترجمة لأنها عبارة عن عبارة expression و ناتج العبارة على اليسار يساوي ناتج العبارة على اليمين ,

لو أننا أعدنا void من عملية المساواة بين str1 و str2 لكان لا معنى لمساواة str3 بها لأننا كأننا نساويها بـ void و لكن في كائن الـ string ما يسمى بالـ Operators Chaining أو كإمكانية عمل سلسة من عملية المساواة كما في المثال الذي في الأعلى,,

و لكن عندما نرجع الكائن نفسه by ref فإننا أوجدنا ميزة التسلسل في الكائن ,,

أخيراُ ,,

يمكن تنفيذ عملية التسلسل بطريقتين أعرفهما ,, الأولى أن يتم التعريف داخل الكائن نفسه أو أن يتم التعريف كـ friend ,,

في كلتا الحالتين سينك تنفيذ ما نريد بطريقتين مختلفتين و على كل حال لن أدخل في تفاصيل الفروق حتى لا يطول الكلام ,,

فلو نظرنا إلى عملية المساواة الثانية يمكن تخيلها على الشكل التالي مع التسلسل :

str2 = str1 ثم أعاد str2 نفسه ثم تمت عملية المساواة الثانية str3 = str2

مثال عن معامل معرف من داخل كائن :

string& operator =( string &NewStr );

و عند كتابة الدالة يمكن أن يعيد الكائن نفسه عن طريق الموشر this المحجوز للتأشير على الكائن الذي نكتب كوده!

return *this;

أما إن كنا نستخدم friend Operator فالتالي هو المطلوب :

friend string& operator =( string &OldwStr, string &NewStr );

و يتم إعادة OldStr by ref

return &OldStr;

تحياتي ,,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 6 ديسمبر 2007 في 13:41

#2

بارك الله فيك .. عندي سؤال قديم .. والى الان محيرني ,,

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

لا اعرف ان كانت لااسئلة لها علاقة قوية مع الموضوع لكن رح اسال :)

سؤالي :

متى أرجع كائن يكون by value أو by reference .

الجواب اللي نعرفه .. هو عن by reference لا ينسخ الكائن ... بالتالي يساعدنا على تسريع البرنامج .

لكن أحيانا افقد القيمة ..

اتمنى هذه الاكواد توضح الاشكال :

Object get()
{
   Object o;
return o;
}

Object& get2()
{
   Object o;
return &o;
}

Object get(Object o)
{

return o;
}


Object& get(Object o)
{

return &o;
}

Object  A::get()
{
return this->o;
}

Object&  A::get()
{
return &this->o;
}

فسؤالي .. هو متى استخدم احد الطرق السابقة ... .. انا افضل ال Reference لانه الانسب للكائن الكبير ,, ولكن أحيانا يموت الكائن .. واحيانا لا .. !!!!

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

لذلك صرت الجأ غالبا الى اعادة الكائن عن طريق البارمترات .. للهروب من المشاكل ..

فهل يوجد قواعد أمشي عليها ... لهذه الحالات ..

أنا لم أتكلم عن عدم فهمي ايضا .. لامور اخرى مثل

const Object& get()

{

..

}

انا اعاني من عدم فهم بعض الطرق لاعادة القيم من الدالة ..

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

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

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#3

ممتاز جدا :)

بالنسبه الى متى يكون الأرجاع بالقيمه ومتي يكون بالمؤشر Reference or pointer .

أولا عنما نستقبل الكائن بالقيمه ، هنا معناه أن تنشئ نسخه من الكائن ، واستدعاء Copy Constructor، في كل مره نعيد الكائن بالقيمه ايضا يتم استدعاء Copy Constructor ، وهنا اذا كان حجم الكائن كبيرا ، سيكون تأثيره ملحوظ في سرعه البرنامج واستهلاكه للذاكره ( أقصد المكدس Stack ) .

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

ولكن تبدأ المشاكل في حال انك انشئت كائن في الداله ، ثم أرجعت Reference له ، الان هنا تكون مشكله في أن الكائن اصبح out of scope ولديك Reference ولكن الى أين ؟ وهذه مشكله كبيره جدا .

طبعا في هذه الحاله يمكن ان ننشئ الكائن في الheap أو Free Store باستخدام معامل new ، وهكذا نقوم بارجاع الكائن

بالمرجع ، ولكن أيضا هناك مشكله في ما اذا اردنا حذف الكائن ، كيف نحذف الكائن ، نحن لدينا مرجع له ، وهو موجود في الheap، وطبعا لن تستطيع استخدام delete مع الـ Reference

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

الحل الأفضل هو انشاء الكائن في الداله في الheap ، وارجاع مؤشر له Pointer .

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

ولكنه سيكلفك استدعاء copy Constrcutor في كل مره ترجع فيه بالقيمه .

باختصار ،

عندما ننشئ كائن داخل الداله ، يفضل دائما ارجاعه بالقيمه .

أما اذا أردنا تغيير القيمه الحاليه للكائن ( عندما نتعامل مع المؤشر this داخل الداله) فيفضل دائما ارجاع بReference .

اما اذا اردنا ان ننشئ الكائن داخل الداله ، ونرجعه بطريقه غير القيمه فيفضل ارجاعه بالمؤشر Pointer .

أعتذر عن عدم التوضيح بالكود ، وفي حاله كانت هناك نقطه ما غير واضحه ، يرجي السؤال .

تم تعديل هذه المشاركة بواسطة romansy في 13 ديسمبر 2007 في 18:36

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

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

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

#4

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

أهلاً بأخونا الشمري,

الحقيقة أني كتبت الموضوع بسرعة و عندما قرأته مرة ثانية لم أفهم شيء :lol:

فأنا تكلمت عن عدة أشياء في وقت واحد,

الهم أن السؤال الذي طرحته هو السؤال المطروح أصلاً و أجاب الأخ romansy عنه و سأضع ما عندي

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

/index.php?showtopic=143032

أعتقد أن عملية إرسال المتغيرات إلى الدوال واضحة بالنسبة لك, و لكن عملية الإرجاع هي المشكلة ,,

أولاُ لن نتكلم عن فرق السرعة لأنه ليس بالكبير إن لم يكن هناك كمية كبيرة من البيانات,

عملية الإرجاع بالقيمة, عملية أسهل و أضمن لتجنب الأخطاء, و لكن ماذا يجب أن نأخذ في الاعتبار عند الإرجاع بالمرجع by reference.

أولاُ يجب أخذ المجال الذي عرفت فيه المتغيرات في الحسبان, و بالتالي كل متغير معرف داخل الدالة لا يمكن إرجاعه بالمرجع by ref,

int& func(){
	int x = 3;
	return &x;	// This is not Correct.
}

بالتالي يبقي لدينا ثلاث حالات, الحالة الأولى أن إحدى الـ Parameters قد تم تمريره بالمرجع by ref و بالتالي يمكن إرجاعه بالمرجع أيضاً لأنه غير معرف أصلاُ في مجال دالتنا

أما إذا كان الـ Parameter قد تم تمريره للدالة بالقيمة, فلن يصلح إرجاعه by ref لأن النسخة التي حصلنا عليها خاصة بالدالة فقط أي متغيرات محلية للدالة.

Scope.

int& func( int &x ){
	return &x; // This is Correct. 
}

الحالة الثانية عندما يكون لدينا مؤشر معرف داخل الدالة و قمنا بحجز ذاكرة عن طريقه لكائن ما, في هذه الحالة يمكننا إرجاع الذاكرة المحجوزة لأن التعامل مع الـ heap سيكون يدوياً, و أما المؤشر نفسه و ليس الذاكرة التي حجزها سينتهي كأي متغير عادي عند الخروج من مجال الدالة.

int& func(){
	int *x
	x = new int;
	*x = 5;
	return x; // This is Correct. 
}

الحالة الثالثة عندما يكون إحدى الـ Parameters مرسلاُ على شكل مؤشر مثلاً. و بالتالي يمكننا إرجاع الذاكرة التي يؤشر عليها المؤشر بالمرجع by ref.

int& func( int *x ){
	*x = 5;
	return x; // This is Correct. 
}

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

تحياتي للجميع,,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 14 ديسمبر 2007 في 11:34

#5

أهلاُ من جديد ,,

بالنسبة للحالات التي وضعتها, فهي كالتالي ,

Object get()
{
   Object o;
return o;
}

Object& get2()
{
   Object o;
return &o;
}

Object get(Object o)
{

return o;
}


Object& get(Object o)
{

return &o;
}

Object  A::get()
{
return this->o;
}

Object&  A::get()
{
return &this->o;
}

بالنسبة للحالة الأولى فهي صحيحة لأننا نقوم بإرجاع عن متغير محلي,

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

الحالة الثالثة صحيحة, لأننا نقوم بإرجاع نسخة عن Parameter sended by value و هذا الوسيط يمكن اعتباره متغير محلي للتبسيط.

الحالة الرابعة غير صحيحة, لأننا نقوم بإرجاع وسيط مرسل بالقيمة, و هو يعتبر متغير محلي للدالة.

في الحالة الخامسة و السادسة, تم استخدام المؤشر this, و باعتقادي أنك تعرف أنه هذا المؤشر يؤشر إلى الكائن الذي تنتمي إليه الدالة التي نكتبها,

و يمكنك معاملته على أنه مؤشر معرف من نفس نوع الكائن,

لإرجاع الكائن ( الكائن و ليس مؤشر الكائن ) :

return *this;

و لإرجاع المؤشر :

return this;

و يمكنك تطبيق ما في الرد السابق على هذا المؤشر أيضاُ, و لكن الفائدة قد تبدو غير واضحة منه في البداية,

و لكن على سبيل المثال, مع الافتراض أنك تعرف معنى Operators Overloading ..

تصور أن لديك فئة تحميل صورة bmp للذاكرة - قمت بتصميم فئة كهذه إن كنت تريدها :rolleyes: - و نريد إعادة تعريف المعامل "=" لكي نقوم بنسخ صورة موجودة في كائن bmp إلى كائن آخر bmp , سنقوم بكتابة التالي :

void operator =( bmp pic2 );

و إذا أردنا نسخ الصورة لكائن آخر فكل ما علينا فعله هو :

pic1 = pic2;

و لكن ماذا لو أردنا مساواة ثلاث كائنات أو أكثر :

pic1 = pic2 = pic3;

هنا سنقوم بعمل التالي :

bmp& operator =( bmp pic2 );

الفكرة السابقة موجودة في نهاية الموضوع الاصلي ,,

تحياتي و أنا جاهز لتوضيح ما تريده

تحياتي ,,

#6

السلام عليكم ،

شرح واضح أخي خالد ، ما شاء الله عليك ...

لكن حبيت أنبه الى أمر بسيط ،

وهو عندما نرجع كائن أو متغير ما بواسطه الـ Reference ، سوف نرجعه بواسطه اسمه مباشره ، وبدون استخدام علامه الـ Umbersand أو Address of Operator ، لأننا نريد أن نرجع قيمه الكائن بالمرجع ، وليس عنوان الكائن بالمرجع !

فقولك :

اقتباس
int& func( int &x ){

return &x; // This is Correct.

}

هو صحيح في الترجمه ، ولكنه خطأ منطقي ، حيث هنا أرجعنا عنوان المتغير بالمرجع وليس قيمته . واذا كان لدينا في البرنامج الرئيسي Main :

int a = 3;
int z = func(a);
cout << z;

سوف يكون الناتج هنا عنوان المتغير a ، وليست قيمته (3) .

لذلك التصحيح هنا هو :

int& func( int &x ){
	return x; // This is Correct. 
}

ونفس الأمر عندما نرجع المؤشر بواسطه الـ Reference ، يجب أن نستخدم الـ Derefrence (علامه * ) للمؤشر حتى نحصل على قيمته ومن ثم نرجعها بواسطه ال Reference

اقتباس
int& func(){

int *x

x = new int;

*x = 5;

return x; // This is Correct.

}

تصبح

int& func(){
	int *x
	x = new int;
	*x = 5;
	return *x; // This is Correct. 
}

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

لكن حبيت أوضح أنه الحالتين الأخيرتين غير صحيحتين:

اقتباس
Object A::get()

{

return this->o;

}

Object& A::get()

{

return &this->o;

}

حيث في الأولى يبدوا أنك أردت ارجاع قيمه الكائن الحالي ،

لذلك عليك استخدام المؤشر this لانه يشير الى الكائن الذي تتعامل معه (وانت لا تعرف اسمه بالضبط ، لذلك يجب ان تستخدم this ) . هنا كما ذكر أخ خالد عليك باستخدام return *this .

لكن في هذه الحاله أرجعنا قيمه الكائن (الحالي) بالقيمه وهو أمر غير مرغوب به دائما ، حيث يفضل ارجاعه بواسطه المرجع Reference . وليس هذا فقط بل يفضل أن تكون القيمه التي أرجعناها بواسطه المرجع ثابته لا تتغير ، سأتكلم عن const بعد قليل .

اذا تكون الحاله الأولى :

const Object&  A::get()
{
return *this;
}

أما في الحاله الثانيه فيبدوا أنك أردت ارجاع عنوان الكائن الحالي ، وسوف ينطبق نفس الكلام السابق ، لكن بدون استخدام * مع المؤشر this (حتى نحصل على العنوان للكائن ) .

const Object*  A::get()
{
return this;
}

نأتي الأن الى موضوع الـ const Refernce ،

في الحقيقه هناك الكثير من يتخلبط بمفهوم const reference to object و مفهوم reference to const object .

لكن في الأصل انه الـ reference نفسه لا يتغير أبدا بعد تهيئته بقيمه (كائن ، أو متغير) لذلك ال refernce هي دائما ثابته .

اذا في حالة رأينا كلمه const بجوار الrefernce علينا أن نعرف ان الكائن ثابت ولن تتغير قيمته .

أنظر الى الـ prototype التالي :

Object & FunctionTwo ( Object & o);

هنا استقبلنا كائن بالمرجع ، وفي حاله أجرينا تغيير في الكائن o سوف ينطبق التغيير على الكائن الذي ممرناه لهذه الداله .

وفي حاله الأرجاع ، سوف نرجع مرجع للكائن .

واذا كان لدينا في الداله main :

Object z = 10;
Object x = FunctionTwo(z)

الان ارسلنا z الى الداله FunctionTwo ، وفي حاله الداله أردات تغيير z فبمكانها ذلك لانه الكائن o اسم اخر ل z واي تغيير نجريه عليه ينطبق على z .

الان عندما نتتهي عمل الداله ونرجع الكائن ، سوف تكون القيمه العائده في x . وهنا بامكاننا تغيير قيمه x فيما بعد :

x = FunctionTwo(w);

لاحظ أن قيمه x تغيرت هنا بقيمه جديده ، وهو في بعض الأحيان أمر غير مرغوب به ، لذلك يجب أرجاع مرجع ثابت بحيث انه لن يمكننا بعدها من تغيير قيمه x .

const Object & FunctionTwo (const Object & o);

هنا الكائن o ثابت لا تتغير قيمته .

والقيمه العائده (عندما نسندها لكائن أخر ) لن تستطيع تغيرها أيضا .

أتمنى أي يفهم أحد ما قلت ، حتى أنا لم أفهم :) .

أيضا يرجي التصحيح في حاله كان هناك خطأ ما .

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

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

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

#7

رائع ,, أزين من شرح أي دكتور :D .

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

لكن لي تعليق على :

اقتباس
وهو عندما نرجع كائن أو متغير ما بواسطه الـ Reference ، سوف نرجعه بواسطه اسمه مباشره ، وبدون استخدام علامه الـ Umbersand أو Address of Operator ، لأننا نريد أن نرجع قيمه الكائن بالمرجع ، وليس عنوان الكائن بالمرجع !
اقتباس

int& func( int &x ){

return &x; // This is Correct.

}

هذه أعرفها .. لكن نسيت فقط .. بسبب طول مدة انقطاعي عن السي .

أنا مشكلتي هي متى يفقد المتغير قيمته عندما يعود من الدالة ومتى لا يفقدها .. وهو ما أرى جوابه في ردودكم .

سأقرأ الردود وأخبركم بمدى فهمي خلال ال 24 ساعة القادمة ان شاء الله :D .

تم تعديل هذه المشاركة بواسطة الشمري في 14 ديسمبر 2007 في 15:33

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

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

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#8

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

فقد أحببت ان اخبركم اني فهمت :D ...

بما فيها const Reference عندما نعيده من الدالة .. بارك الله فيكم ,,

وحتى اختبر فهمي ... ما رأيكم بهذا الكود :

class B
{
public:
int x;
};

class A

{
public:
B b;
const B& get() // 1
{
return b;
}

const B& get() // 2
{
return b;
}

B& get()  // 3
{
return b;
}

const B get( ) //4
{
return b;
}

};

الطريقة الاولى : نعيد كائن بطريقة const Reference .. حيث استخدمنا ال Reference للسرعة و const حتى لايتغير الكائن b من خارج الكلاس .

الطريقة الثانية : استخدمنا الReference للسرعة .. ولكن الكلاس معرض للتغيير .

الطريقة الثالثة : لم نستخدم الReference ... ولم نستخدم ال const .. سنخسر السرعة ..( نسبيا) .. ولا اعرف هل الكائن معرض للتغيير في هذه الحالة من خارج الكلاس .

الطريقة الرابعة: لن يتغير الكائن من خارج الكلاس .. ولكن لم نستخدم الReference ..

عموما يبدو أني بدأت اخربط ... خلونا ساكتين أحسن :D ... بس والله .. هالامور الدقيقة تحيرني .. بالرغم من ان البدائل موجودة ...

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

تم تعديل هذه المشاركة بواسطة الشمري في 16 ديسمبر 2007 في 22:23

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

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

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#9

أهلاً بك أخي الشمري,

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

للأسف دائماُ ما تتشابك الخيوط في رأسي عندما أتكلم عن ++C :wacko:

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

1 ) الكائن الأصلي تم إرجاعه و لا يمكننا تغيير البيانات فيه.

2 ) كالأول :)

3 ) الكائن الأصلي تم إرجاعه مع إمكانية تعديل البيانات فيه.

4 ) تم إرجاع نسخة عن الكائن الأصلي و النسخة التي أرجعناها غير قابلة للتعديل.

بالنسبة للمثال الرابع فلا أعتقد أنك سترى مثل هذا الكود أبداً

تحياتي ,,

#10

رائع ما تصنعه

مشاركة رائعة واسلوب محفز

تسحق الشكر والإشادة , مني جزيل الشكر

وليس يبني العلا الا نديٌ ووغى , همـا المعارج للأسنى من الرتب

#11

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

الحقيقة الموضوع جميل و المشاركات عميقة, اتمنى ان تدوم الاسئلة و المناقشات فى مستويات كهذه و الى الامام ان شاء الله

#12
اقتباس
2 ) كالأول

:lol:

عفوا .. حدث خطأ في ترتيبي للدوال وتعليقي عليها .. هذا هو الشكل الصحيح للدالة الثانية والثالثة

بالتالي ,, أعد النظر في تعليقي في المشاركة السابقة بناء على الترتيب التالي ,,

B& get() // 2
{
return b;
}

B get()  // 3
{
return b;
}

:happy:

شكرا لك أخوي خالد على التوضيح والتعليق ,

تم تعديل هذه المشاركة بواسطة الشمري في 17 ديسمبر 2007 في 20:20

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

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

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#13

السلام عليكم

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

يعني الشرح هنا بعيد عن البرمجة الكائنية OOP. وكيف لو كنا نريد استرجاع مرجع لمتغير محلي A reference to a local variable.

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

(مقتبس من كتاب C++ How To Program)

بعد ما قرأت هذا الكلام من خلال الكتاب هذا, للأسف لم أجد عينة لبرنامج يشرح و يوضح هذا المفهوم. لكن انا باشرت البحث والتطبيق لحدة ما فهمت السالفة وعملت البرنامج التالي :

//Returning References
//Authored by Raghad to illustrate Returning References from functions.

#include <iostream>
using std::cout;
using std::endl;


int &reRef(); //returns a refernce to an int variable.

int main()
{
   int &refX = reRef();

   //Notice that refX is refers to x within reRef().
   //That is examined by printing the addresses of each other.
   cout << "refX address is: " << &refX << endl;
   cout << "x address within reRef() is: " << &reRef() << endl;
   cout << endl;

   //Now assign refX a new value (10);
   refX = 10;
   cout << "refX value is: " << refX << endl;
   //Now x value within reRef()  has been changed to 10.
   cout << "x is: " << reRef() << endl;
   cout << endl;

   //The new thing here is that we can use reRef() as 'lvalue'
   //It means that we can use reRef() function as left value.
   //As the following:
   reRef() = 22;
   //this will effect the value of both refX and x itself.
   cout << "refX value is: " << refX << endl;
   cout << "x value is : " << reRef() << endl;

   return 0;
}


int &reRef()
{
   //The returned refernce should be declared static.
   static int x = 0;//static variables should be initialized

   return x;//This will return a reference to x;
}

البرنامج بكل بساطة عبارة عن إجراء يقوم باسترجاع عنوان (مرجع) لمتغير محلي وهو في هذه الحالة المتغير x الموجود داخل الإجراءreRef(). لاحظوا بالفعل كنت مضطرة لجعل هذا المتغير متغير محلي ساكن static variable. لأني أثناء التجربة كان المتغير يموت بمجرد انتهاء النطاق تبع الإجراء الخاص به.

انتقالاً إلى الإجراء الرئيسي main. لاحظوا كيف انشأت مرجع refX والذي سيحمل عنوان (مرجع) المتغير x الموجود في قلب الإجراء. لاحظوا كيف اتأكدت من الكلام هذا بواسطة طباعة عنوان كل من المرجع refX و الإجراء reRef() اللي بدوره يرجع عنوان للمتغير المحلي الساكن x.

الآن أي تغيير أو تعديل في قيمة المرجع refX راح يغيير و يعدل قيمة المتغير المحلي x ايضاً. والعكس صحيح.

الغريب بالإجراءات التي تقوم بإسترجاع مرجع مثل الإجراء reRef() في البرنامج هذا, أنه يمكننا استخدامها في الجانب الأيسر من علامة التعيين = وهذا يعني اننا نستطيع استعمالها وتعديل قيمها بالضبط مثل المتغيرات العادية, لاحظوا في السطر التالي من البرنامج

اقتباس
reRef() = 22;

لاحظوا كيف أثر هذا السطر على قيمة x وبالتالي على قيمة المرجع المتعلق به وهو refX.

مع تمنياتي لكم بالتوفيق

تم تعديل هذه المشاركة بواسطة رغـــــــــد في 17 ديسمبر 2007 في 22:58

[وسط]

♥ Countess ♥

57899411.gif

♥

[/وسط]

#14

شكراُ لك أخت رغد, لم أفكر ابداُ في استعمال المتغيرات الـ static لإرجاع متغير بالمرجع :)

#15
اقتباس
لاحظوا كيف أثر هذا السطر على قيمة x وبالتالي على قيمة المرجع المتعلق به وهو refX

هذا الكلام مفيد والله اعلم في اللصق المتكرر للكائن .

والظاهر انه يستخدم بشكل مفيد مع المعاملات الثنائية عندما نريد تحميلها بشكل زائد .

فبدلا من كتابة :

z = n;
 m = n;

تكتب :

 z = m = n;

والله اعلى واعلم وأجل واحكم .

ونهنئ الأخوة بالعيد .

أتراك عين والعيون لها مدى *** ماجاوزته ولامدى لمداكا

إن لم تكن عيني تراك فإنني *** في كل شيء أستبين علاكا

يامنبت الأزهار عاطرة الشذى *** هذا الشذى الفواح نفح شذاكا

يامجري الأنهار ماجريانها *** الا انفعالة قطرة لنداكا

يارب هذا العصر الحد عندما *** سخرت ياربي له نعماكا

او مادرى الانسان ان جميع ما *** وصلت اليه يداه من نعماكا

لو شئت ياربي هوى صاروخه *** او لو أردت لماستطاع حراكا

فليرض عني الناس او فيسخطوا *** انا لم اعد اسعى لغير رضاكا

ذقت الهوى مرا ولم اذق الهوى *** يارب حلوا قبل ان اهواكا

يارب عدت الى رحابك تائبا *** مستسلما مستمسكا بعراكا

اتردني وترد صادق توبتي؟ *** حاشاك ترفض تائبا حاشاكا

فأقبل دعائي واستجب لرجاوتي*** ماخاب يوما من دعا ورجاكا

ياايها الإنسان مهلا مالذي *** بالله جل جلاله أغراكا؟

فاسجد لمولاك القدير فإنما *** لابد يوما تنتهي دنياكا

---------------------------------------------------------------

لا إله إلا أنت سبحانك إني كنت من الظالمين

#16

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

شرح ممتاز وفقكم الله...

و بما أن سؤالي يتعلق بالموضوع ..و نقطة لم نذكرها..

و هي أن بيانات الأعضاء في الفئة من الخطأ المنطقي ارجاعها من احد الدوال by reference

فلماذ؟؟؟؟

و جزاكم الله خيرا

تم تعديل هذه المشاركة بواسطة المشتاقة للرحمن في 16 يناير 2008 في 23:56

#17

عليكم السلام

------------

اهلا بك اختي

-------------

بالطبع و بصفة استرجاع By Reference مطلوب تتجنبيه في الأشياء العادية ما بالك بـ Data members؟

بالطبع السبب لأنه رح يكسر التغليف Breaking The Capsulation وهي من أهم الميزات اللي لازم تتوفر بالبرمجة الكائنية. وبالطبع الخبراء ينصحونا بل طبيعة تقنية البرمجة الكائنية تفرض علينا أن نجعل البيانات الموجودة بالفئة Class مغلفة ومحفوظة وآمنة وبعيدة عن متناول أيدي الزبون The Client.

يعني باختصار اختي الفاضلة حالات أو بيانات الأعضاء Data members زي ما تفضلتي لازم تكون آمنة ومخفية عن المستخدمين وإرجاعها بواسطة المرجع يكسر هذا الأمان وهذا التغليف

----------------------

مع تمنياتي لك بالتوفيق

[وسط]

♥ Countess ♥

57899411.gif

♥

[/وسط]

هذا الموضوع مغلق.

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