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

المؤشرات كقيمه معاده من الدوال

بدأه C++er في 7 أكتوبر 2009 · 13 رد · 1,138 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

يعتقد البعض انه لتعود بمؤشر من الداله لابد ان يتم حجز ما يشير له هذا المؤشر بالكلمه new و ذلك حتى يتم انشاء المساحه لهذا الكائن داخل الـ heap و يصبح متاحا التعامل معه بعد الإنتهاء من الداله.

ايضا كل الكتب تحذر بشده من ارجاع مرجع لكائن تم انشائه داخل داله حيث انه بعد الخروج من الداله يصبح المؤشر غير سليم و يصبح المؤشر يشير إلى garbage.

يوجد 3 انواع من الكائنات يمكن انشائهم داخل الـ C++:

النوع الأول هو automatic object و هو ما يقوم المترجم بحجز مساحه له و لكن يقوم هو بحذفه بعد ان يتم تجاوز المدى الذى تم التصريح عنه فيه.

النوع الثانى هو dynamic object و هو النوع الذى يتم حجز مساحته بإستخدام new و لا يتم تحرير هذه المساحه إلا بإستخدام delete او بإنتهاء البرنامج.

النوع الثالث هو static object و هو يتم حجز مساحه له قبل بدء البرنامج فى العمل و لا يتم حذفه إلا بإنتهاء البرنامج.

فكره هذا الموضوع هى كيفية ارجاع automatic object من الداله دون ان يتم حفه من الـ stack.

انظر هذا المثال

 

[color= #007f00;]#include <iostream>

[color= #0000ff;]using [color= #0000ff;]namespace std;

 

 

[color= #0000ff;]struct Point2D

[color= #000000;]{

	[color= #0000ff;]float x, y;

 

	Point2D[color= #000000;]([color= #000000;]) [color= #000000;]: x[color= #000000;]([color= #ff0000;]0[color= #000000;]), y[color= #000000;]([color= #ff0000;]0[color= #000000;]) [color= #000000;]{[color= #000000;]}

 

	Point2D[color= #000000;]([color= #0000ff;]float _x, [color= #0000ff;]float _y[color= #000000;]) [color= #000000;]: x[color= #000000;](_x[color= #000000;]), y[color= #000000;](_y[color= #000000;]) [color= #000000;]{[color= #000000;]}

[color= #000000;]};

 

 

Point2D[color= #000000;]* tmp;

 

Point2D[color= #000000;]* getPoint[color= #000000;]([color= #000000;])

[color= #000000;]{ [color= #0000ff;]return [color= #000000;]&Point2D[color= #000000;]([color= #ff0000;]3.14f,[color= #ff0000;]2.71f[color= #000000;]); [color= #000000;]}

 

 

[color= #0000ff;]void PrintData[color= #000000;]([color= #000000;])

[color= #000000;]{

	cout [color= #000000;]<< [color= #A31515;]"from PrintData()n";

 

	Point2D[color= #000000;]* pt [color= #000000;]= getPoint[color= #000000;]([color= #000000;]);

	cout [color= #000000;]<< [color= #A31515;]"X = " [color= #000000;]<< pt[color= #000000;]-[color= #000000;]>x [color= #000000;]<< [color= #A31515;]", Y = " [color= #000000;]<< pt[color= #000000;]-[color= #000000;]>y [color= #000000;]<< endl;

 

	pt[color= #000000;]-[color= #000000;]>x [color= #000000;]= [color= #ff0000;]5;

	pt[color= #000000;]-[color= #000000;]>y [color= #000000;]= [color= #ff0000;]7;

 

	tmp [color= #000000;]= pt;

[color= #000000;]}

 

 

[color= #0000ff;]void main[color= #000000;]([color= #000000;])

[color= #000000;]{

	PrintData[color= #000000;]([color= #000000;]);

 

	cout [color= #000000;]<< [color= #A31515;]"from main()n";

	cout [color= #000000;]<< [color= #A31515;]"X = " [color= #000000;]<< tmp[color= #000000;]-[color= #000000;]>x [color= #000000;]<< [color= #A31515;]", Y = " [color= #000000;]<< tmp[color= #000000;]-[color= #000000;]>y [color= #000000;]<< endl;

[color= #000000;]}

نتيجة هذا البرنامج هى (على جهازى)

from PrintData()

X = 3.14, Y = 2.71

from main()

X = -1.07374e+008, Y = -1.07374e+008

Press any key to continue . . .

فى البدايه قمت بصنع Point2D و التى تمثل نقطه ثم بعدها قمت بإنشاء الداله التى ستعود بمؤشر لكائن من هذا النوع و هى الداله getPoint.

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

داخل الداله main يتم استدعاء الداله PrintData و التى بدورها تقوم بإستدعاء getPoint و بعدها تطبع القيم و تحتفظ بقيمة المؤشر داخل المتغير tmp بعدها يتم العوده للداله main مره اخرى و التى تقوم بطباعة ما يشير إليه المتغير tmp.

فى المره الأولى التى تم طباعة x و y من داخل PrintData تم طباعة قيم صحيحه اما عند طباعة القيم الموجوده بنفس المكان من الداله main ظهرت لنا garbage.

عندما تقوم بإنشاء متغير داخل scope معين يتم الإحتفاظ بهذا الكائن حتى يتم الخروج من هذا الـ scope هذه القاعدة تطبق فى جميع الحالات مع الـ automatic obhect إلا فى حالة واحده و هى مع الكلمه return حيث فعليا القيمه العائده من return هى لا تنتمى للـ scope الذى تم كتابة return فيه و إنما إلى الـ scope الذى تم استدعاء الداله التى مكتوب فيها return.

قد يقول البعض إذا من الممكن تعريف كائن داخل scope معين ثم العوده به عن طريق return؟ لا هذا لن يحدث و السبب انظر حيث تم التصريح عن هذا الكائن فإن كان امام return فهو لا ينتمى لهذه الداله و لكن للمكان الذى تم استدعاء الداله منه و إن كان داخل الداله فهو ملك للداله و سوف يتم تدميره بمجرد الخروج من الداله.

و لهذا فقد قمت بإرجاع عنوان كائن تم انشائه امام return و بالتالى هذا الكائن لا ينتمى للـ scope الخاص بالداله getPoint و إنما للداله PrintData.

لذلك عندما قمت بالتعامل مع ما يشير له هذا المؤشر من داخل الداله main كان هذا خارج مجال تعريفه لذا لم يكن موجود لإنه تم حذفه بمجرد الخروج من الداله PrintData.

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

و الله ولى التوفيق

تم تعديل هذه المشاركة بواسطة Muhammad alaa في 7 أكتوبر 2009 في 08:20

مدونتي: C++ Tips and Tricks

#2

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

الطريقة التي ذكرتها قد تندرج تحت تصنيف "سلوك غير معرّف" (Undefined Behavior) أي إنها ليست جزءاً من قواعد اللغة لذلك فقد يتصرف كل مترجم بطريقة مختلفة، وأحياناً إصدارات نفس المترجم المختلفة تتصرف بطرق مختلفة.

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

قد أكون مخطئاً، لذلك سأترك الساحة لمن هو أعلم مني بمعايير لغتي ++C/C.

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

#3
اقتباس
الطريقة التي ذكرتها قد تندرج تحت تصنيف "سلوك غير معرّف" (Undefined Behavior) أي إنها ليست جزءاً من قواعد اللغة لذلك فقد يتصرف كل مترجم بطريقة مختلفة، وأحياناً إصدارات نفس المترجم المختلفة تتصرف بطرق مختلفة.

جميع المترجمات عندما تقوم بإرجاع نسخه من فئه معينه عند تعريف return فهم يقوموا بإنشاء نسخه و تمريرهل للدالة فى المستوى الاعلى من هذه و حيث ان هذا الكائن تم التصريح عنه و تعريفه عند الكلمه return اى وقت الخروج من الداله إذا هو لا ينتمى للداله و لكن ينتمى للمدى الذى اعلى من هذه الداله بمعنى هذا الكائن ينتمى للـ scope الذى سيعود الـ execute control لها و سيظل هذا الكائن متاح طالما ان الـ scope الذى هو موجود به متاح.

كل ما حدث هو انى بدلا من ان اعود بالكائن نفسه قمت بالعوده بموقع هذا الكائن لذا فالفكره واحده سواء عدت بالكائن او بمؤشر له سيظل هذا الكائن متاح حتى انتهاء الـ scope الموجود به.

و الله ولى التوفيق

مدونتي: C++ Tips and Tricks

#4

السلام عليكم ...

بالفعل أخ محمد, هذا التصرف غير معرف بالمواصفات القياسية. أنت تقوم بإعادة عنوان منطقة في الذاكرة لم تعد ملكك. الـ deconstructor يتم استدعاؤه بعد عملية الـ return. و return تقوم بإعادة نسخة من العبارة التي تليها.

بمعنى أننا أعدنا نسخة من عنوان الكائن الذي تم إزالته من الـ stack.

Point2D* getPoint()
{
	return &Point2D(3.14f,2.71f);
} // deconstructor of Point2D is called after return.

الإرجاع بالقيمة ليس سيئاً كما يعتقد البعض, معظم مترجمات ++C تطبق RVO دون شعور المبرمج للتخلص من النسخة الزائدة عندما يصل التنفيذ إلى return.

عموماً, هذه مدونة رائعة لأحد أشهر أعضاء boost يتكلم بالتفصيل عن الموضوع:

Want Speed? Pass by Value.

موضوع رائع للمناقشة,

تحياتي..

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

#5
اقتباس
بالفعل أخ محمد, هذا التصرف غير معرف بالمواصفات القياسية.

فعلا هى كذلك.

اقتباس
أنت تقوم بإعادة عنوان منطقة في الذاكرة لم تعد ملكك. الـ deconstructor يتم استدعاؤه بعد عملية الـ return. و return تقوم بإعادة نسخة من العبارة التي تليها. بمعنى أننا أعدنا نسخة من عنوان الكائن الذي تم إزالته من الـ stack.

كما قلت من قبل اخى خالد الكائن الذى يتم انشائه عند return هو ليس ملك الداله الحاليه و انما ملك الداله التى استدعت الحاليه. حتى لا يختلط علينا الأمر سأسمى الداله الحاليه هى X و الداله التى استدعتها هى Y.

الداله Y قامت بإستدعاء الداله X فيتم الإنتقال لمكان كود الداله X داخل code area الخاص بالبرنامج، بعدها يتم تنفيذ كود الداله و اثناء احتياج الداله لمتغيرات يتم إنشائها داخل الـ stack فى الجزء الخاص بالداله X، داخل كود الـ c++ عندما نصل للكلمه return و يحدث ان تكون متبوعه بإنشاء نسخه جديده من كائن فيتم حجز مساحه من الذاكره ليتم تخزين محتوى هذا الكائن بها، هذه المساحه لا تتبع مساحة المتغيرات الخاصه بـ X حتى لا يتم حذفها بإنتهاء تنفيذ الداله، بعده انشاء الكائن يتم تخزين موقع هذا الكائن داخل المسجل eax بعدها نصل للأمر retn فيتم عندها اخلاء منطقة الذاكره الخاصه بالداله X من الـ stack و يبقى لدينا موقع الكائن الذى تم انشائه داخل الـ stack موجود بـ eax، بعد انتهاء استدعاء الداله يتم الإنتقال إلى التعليمه التاليه و لنفترض انها mov لتنقل موقع الكائن من eax إلى مكان اخر و هو المتغير الذى سيتم تخزين موقع القيمه المعاده به (انا بتكلم على اساس ان اللى راجع هو كائن مش نوع بسيط) بعد انتهاء الداله Y من العمل يتم اخلاء الـ stack و عند الوصول للمتغير الذى يحتوى على القيمه المعاده يتم اخلاء ما يشير إليه و يتم الخروج من الداله Y.

و الله ولى التوفيق

تم تعديل هذه المشاركة بواسطة Muhammad alaa في 8 أكتوبر 2009 في 04:36

مدونتي: C++ Tips and Tricks

#6
اقتباس
يعتقد البعض انه لتعود بمؤشر من الداله لابد ان يتم حجز ما يشير له هذا المؤشر بالكلمه new و ذلك حتى يتم انشاء المساحه لهذا الكائن داخل الـ heap و يصبح متاحا التعامل معه بعد الإنتهاء من الداله.

ليس لابد ولكنها تعمل(ربما المبرمجون معتادون عليها لانهم معتدون على فعل ذلك مع malloc) .

معلومة مفيدة (ان المتغيرات المنشئة فى سطر الreturn من callee ملك للcaller function )

شكرا لك

تم تعديل هذه المشاركة بواسطة apex في 8 أكتوبر 2009 في 06:32

name : mohamedyosry

#7

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

في البداية سألت أحد المبرمجين الذين هم أعلم مني بـ ++C/C وهو Josh Petrie أحد المطوّرين الرئيسيين لمكتبة SlimDX بخصوص فكرة إن ما يعرّف ضمن return يعود لمدى الـ caller وليس الـ callee وكان جوابه كالآتي:

josh petrie كتب:

The return statement is not elevated to the enclosing scope, that is clearly impossible, else you could never return a value defined locally, since the return expression belongs to the outer scope!

The standard says nothing of any kind of scope elevation when discussing the return statement. Furthermore, it says nothing of any kind of scope elevation or exceptions to the scope-related lifetime rules when discussing lexical scoping constructs (blocks, a.k.a, { and })

عندما أخذت الكود الذي وضعه الأخ محمد وترجمته على GCC حصلت على النتائج الآتية:

E:\Experimenting\return_scope_gcc\main.cpp||In function `Point2D* getPoint()':
		E:\Experimenting\return_scope_gcc\main.cpp|16|warning: taking address of temporary
		E:\Experimenting\return_scope_gcc\main.cpp|33|error: `main' must return `int'
		E:\Experimenting\return_scope_gcc\main.cpp|33|error: return type for `main' changed to `int'
		=== Build finished: 2 errors, 1 warnings ===

وطبعاً عندما نفذت البرنامج ظهرت النتائج خاطئة في الحالتين (حالة PrintData وحالة main)، وذلك يؤكد النقطة التي ذكرها الأخ خالد حول إعادة temporary.

ولكن... عندما ترجمت نفس الكود على Visual Studio 2008 لم يعطني أي خطأ أو تحذير، وحصلت على نفس النتائج التي ذكرها الأخ محمد، وذلك سبّب لي الحيرة، لماذا لم يظهر Visual Studio أي شيء رغم إنه ليس بهذا السوء كمترجم؟

تطلب مني إكتشاف السبب أخذ عدة إحتمالات في الحسبان، أولها إن VS يقوم بتحويل عملية الـ return إلى عملية ضمنية (inline)، ولكن ذلك لم يكن السبب.

ولم أستطع التوصل للسبب حتى وجهني أحد المبرمجين نحو المزايا المحمولة من VC6، حيث إن VC6 كما تعرفون من أسوأ المترجمات التي تم إصدارها من ناحية قوانين اللغة، ولكن كيف ولماذا يعمل Visual Studio 2008 بنفس طريقة VC6 بهذا الخصوص رغم إنها خاطئة؟

أقدّم لكم توسعات ++C الخاصة بمايكروسوفت (language extensions) وهي ميزة ومبدأ شريرين برأيي، هنالك خيار بسيط صغير من Yes أو No تجدوه في إعدادات المشروع، تحديداً في ++C/C ثم Language، الخيار الأول هو: Disable Language Extensions وكل ما عليكم فعله تشغيل هذا الخيار وإعادة بناء الكود...

وما هي نتيجة الترجمة الآن؟

			   1>------ Rebuild All started: Project: return_scope, Configuration: Debug Win32 ------
				1>Deleting intermediate and output files for project 'return_scope', configuration 'Debug|Win32'
				1>Compiling...
				1>main.cpp
				1>e:\experimenting\return_scope\return_scope\main.cpp(16) : error C2102: '&' requires l-value
				1>e:\experimenting\return_scope\return_scope\main.cpp(32) : warning C4326: return type of 'main' should be 'int' instead of 'void'
				1>Build log was saved at "file://e:\Experimenting\return_scope\return_scope\Debug\BuildLog.htm"
				1>return_scope - 1 error(s), 1 warning(s)

لقد إعتبره خطئاً وليس حتى تحذير!

لماذا تم تعطيل خيار Disable Language Extensions هذا بشكل تلقائي؟ لا علم لي...

تحياتي لكم

إضافة:

- تفاصيل Microsoft Extensions

- بارمترات /Za و /Ze (لاحظ الطريف في الموضوع إن البارميتر الشرير Ze مؤشر كـ deprecated منذ VS 2005 ومع ذلك فهو default حتى في VS 2008!)

تم تعديل هذه المشاركة بواسطة SandHawk في 8 أكتوبر 2009 في 13:18

#8

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

و لكن مالا استطيع فهمه هو القيمه المعاده لا يتم نسخها من مكان لأخر فى الذاكره و هذا يعنى ان المترجم يقوم خلسه بعمل cast لمؤشر القيمه المعاده المسجله داخل eax لنوع القيمه المعاده و يسجلها داخل المتغير الذى يستقبل القيمه و مع ذلك هو يرفض ان نحصل على مؤشر القيمه المعاده ؟؟

مع العلم ان القيمه المعاده من return فى هذه الحاله ليست temporary حيث ان الـ temporary لا يتم التعامل معه من قبل المبرمج هو فقط ملك للمترجم يقوم بإنشائه و بحذفه .. و لكن مره اخرى اجد ان القيمه المعاده من return ينطبق عليها فعلا خصائص الـ temporary حيث انك لا تستطيع حذفه او اخذ موقعه .. و لكن يمكن التعديل عليه.

الأمر محير بالنسبه لى لذا احتاج مراجعة الـ standard الخاصه بالـ c++ بخصوص هذا الأمر و لى عوده مره اخرى.

مدونتي: C++ Tips and Tricks

#9

اتضح الأمر و انا كنت مخطئ فى بادى الأمر :wub: و كله بسبب هذه التوسعات الخاصه بميكرو-زفت :angry:

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

هذا كلام خاطئ، لإنه يتم نسخها ايا كان الوضع لإن القيمه المعاده فى حالة by value تعتبر tempoaray و بمجرد العوده يتم نسخها للـ caller و حذف الـ temporary

شكرا للأخ خالد و الأخ سلوان على توضيحاتهم

و دائما كده اغلط علشان استفاد من خبراتكم، جزاكم الله خيرا

و الله ولى التوفيق

مدونتي: C++ Tips and Tricks

#10

سأستغل الفرصة وأسأل .. :) .

المشكلة هي بالسي بلس ، حيث عندما يوجد لدينا دالة تقوم باعادة كائن returning value .. فيجب أن تعيد هذه الدالة قيمة ،

لكن لو كان من المفترض أن لا تعيد قيمة .. فماذا نفعل ؟

مثال / حساب نقطة التقاطع ، ماذا لو لم يوجد نقطة تقاطع .. ماذا نفعل ؟

هل واجه أحد مشكلة كهذه ، ماهو الحل المناسب لمثل هذه الحالات ؟

Point2D getIntersectionPoint(Line l1 Line l2)
{ 
   if( THERE_IS_Intersection )
	   return Point2D(3.14f,2.71f); // IntersectionPoint
   else
   return ?????????????
}

هناك أكثر من حل :

- استخدام البارمترات في تخزين نقطة التقاطع ، فاذا لم توجد نقطة تقاطع تعيد false .

- استخدام كائن اعتباطي يدل على عدم وجود نقطة تقاطع

#define DUMMY_POINT Point2D ( 100000,-123123)
Point2D getIntersectionPoint(Line l1 Line l2)
{ 
   if( THERE_IS_Intersection )
	   return Point2D(3.14f,2.71f); // IntersectionPoint
   else
   return DUMMY_POINT;
}

- الحل الاخير .. هو أن تعيد الدالة مؤشر .. أي نعيد دائما عنوان الكائن بعد أن نعمل new داخل الدالة أو NULL .. لكن حينها يقع حمل تنظيف الذاكرة على caller .

لا أعرف ان كان هناك من واجه هذه المشكلة ( هنا تظهر قيمة الجافا ;) ) .

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

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

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

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#11

السلام عليكم ...

اقتباس
ولكن... عندما ترجمت نفس الكود على Visual Studio 2008 لم يعطني أي خطأ أو تحذير

VC مرن قليلاً :P

و لكن من الخطأ العمل عليه دون رفع مستوى الـ warning, قم برفع مستوى الـ warning إلى Level 4 /W4, و ستظهر العلة بكل بساطة.

يا شباب, ما يتم إعادته هو "نسخة" من العبارة التي تلي return. في مثال الأخ محمد, العبارة تتلخص إلى عنوان في الذاكرة. و كما تلاحظ الكائن المنشأ هو كائن محلي, سواء كان مؤقتاً أم لم يكن.

أخي الشمري,

اقتباس
مثال / حساب نقطة التقاطع ، ماذا لو لم يوجد نقطة تقاطع .. ماذا نفعل ؟

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

يمكنك إضافة دالة جديدة, مثلاً isValidPoint. و تضع قيمة داخلية في point كـ bool مثلاً و تترك للمستخدم عملية التحقق باستخدام تلك الدالة, فهذا سيوضح منطق البرنامج بدلاً من التحقق أن القيمة العائدة null أو غيره.

بالطبع لو أنكم تستخدمون boost كان بإمكانكم استخدام optional, جاهزة مجهزة :P

تحياتي...

#12

أوافق إن optional مثالية لهذه المهمة أيضاً، ولكن يمكن مؤقتاً استخدام أي إسلوب حتى يدخل boost في العمل.

كما إن كل شيء من المحتمل أن يتغير لذلك لا يهم إن تم تأجيل كتابة إحدى الإجراءات حتى بعد، أو تم تغييرها في ما بعد. :)

#13

الحل فى هذه الحاله ممكن ان يكمن فى استخدام calling convention مثل __stdcall يمكنك مراجعة هذا الموضوع على موقع code project لمزيد من التفاصيل.

و لكن فى هذه الحاله ستصبح مكتبة الرياضيات لدينا مثل مكتبة الـ D3DX تحتوى على مجموعه من الكائنات و مجموعة دوال للعمل على هذه الكائنات

مدونتي: C++ Tips and Tricks

#14

boost ورانا ورانا :) .

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

لكن المشكلة هي بشكل الكود الناتج ممكن يتعقد قليلا ..

بالنسبة للمحرك و boost :

سنناقش هذا الموضوع عندما نناقش عن دور boost في المحرك .. لم أتوقع أن يكون لـ boost هذا القدر من المشجعين :-) .. لذلك يمكننا مناقشتها في النسخة الاولى من المحرك لكن بعد أن ننهي الاساسيات ولنا عودة ان شاء الله .

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

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

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

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