في هذه الصفحة
المقدمة
ال Move Semantics تمت إضافتها في C++11 عشان تحل مشكلة معينة والمشكلة كانت الاّتي لو عندنا الcode ده
كود عادي هيعمل vector و في السطر التاني هينسخ copy كل حاجة من الvector الأولاني في الvector التاني وهيعمل pointer جديد و ينسخ الsize والcapacity بتاعت الvector الأولاني زي ما موضح في الصورة
و الoutput هيطلع كالأتي:
هيبقي نسخة طبق الأصل و ده ممكن فعلاً يكون اللي أنا عايزه بس لو افترضنا أنا عايز بدل منسخ كل حاجة أنقلها عشان أنا مثلاً خلاص مش هستخدم أول vector في حاجة تاني فا بدل ما تضيع وقت في نسخ أنا أنقلها أحسن
الفكرة ورا عملية النقل بسيطة أعمل pointer جديد أخليه يشاور علي المكان اللي بيشاور عليه الpointer القديم و منساش أشيل delete طبعاً الpointer القديم عشان لو منستش ممكن يحصل مشكلة double free عشان بحاول اعمل free لنفس الpointer مرتين و ده هيحصل لما الdestructor بتاع ال 2 vectors يتعملهم call في نهاية الscope
هو في الحالة ديه لسه هننسخ الsize والcapacity بتوع الvector الأولاني بس دول أرقام فا مش هيأثروا على الأداء جامد المشكلة في الأداء هتحصل لما أحاول انسخ array أو string أو object مليان data كتير ومن هنا جت فكرة الMove semantics احنا عايزين حاجة نقدر نفرق ما بين لما أكون عايز أعمل copy و عايز أعمل move و الsnippet اللي جاية بتوضح ازاي
الoutput هيطلع كالأتي:
هنلاحظ دلوقتي ان الdata اكنها اتنقلت من v1 ل v2 بس ايه السحر اللي في `std::move` اللي خلا الموضوع ده يحصل بس قبل منفهم لازم نتعرف على الفرق بين حاجتين اسمهما lvalues و rvalues
LValues Vs Rvalues
من الاسم يبان ان الlvalue دايماً علي الشمال و الrvalue دايماً علي اليمين بس في الحقيقة الدنيا مش ماشية كده بظبط يعني في الsnippet ديه
في المثال ده ال s اللي علي اليمين هي lvalue و ال s + s اللي علي الشمال هي rvalue بعيداً عن ان الكود ملوش معني اوي بس هو كود هيcompile عادي
بس فيه قاعدة حلوة بتحاول تفرقلنا بين الlvalue و الrvalue و هي كالاتي الlvalue أنت دايما اللي مسميه في الحالة ديه انا اللي مسمي الstring s لكن ال s + s ديه أياً يكن هي ايه أنا مسمتهاش بمعني اخر الs + s الcompiler كده كده خلف الكواليس هيعمل variable علي الstack ممكن يسميه tmp مثلاً و يكون هو ده اسم s + s فا في الحالة ديه مش انت اللي سميت ايا يكن القيمة بتاعت s + s.
كمثال تاني
في المثال ده الx هتبقي lvalue عشان أنا اللي مسميها و ال 7 + 7 هتكون rvalue عشان انا مسمتهاش
و ممكن نتأكد بمثال بسيط كالتالي:
الكود ده لو حاولت اعمل compile هيجبلي error غريب جداً
الerror بيقول أنا function باخد lvalue بس و أنت باعتلي rvalue يعني أكن الparameter كان int و أنا بعتله string مثلاً فا كأني باعت type غلط للfunction.
ايه علاقة الكلام ده بقي بالmove semantics الفرق بين الlvalue و الrvalue هو اللي هيخلينا نفرق أنا عايز أعمل copy ولا move
عن طريق اني و أنا بعمل class جديد هعرف حاجتين زيادة غير الconstructor و اليdestructor و الcopy assignment و الحاجتين دول هما الMove constructor و الMove assignment operator و الخمس حاجات دول في C++ بيتسموا "The rule of five" و ديه functions المفروض تعرفها في أي class تعمله
مثال عملي
في المثال ده عملنا class جديد اسمه Car و فيه id وname و pointer واخر سطرين هما الMove constructor و الMove assignment operator وبياخدو كاarrgument حاجة شكلها عجيب فيها 2 علامة & ده بيتسمي الrvalue reference و هو ده اللي بيفرق لما اجي اعمل assignment(اللي هي علامة '=')
اعمل call للcopy assignment operator ولا الMove assignment operator حسب الحاجة اللي أنا باعتها هي lvalue ولا rvalue احنا في الأول خالص استخدمنا مع الvector في أول مثال خالص std::move لما كنا عايزين ننقل الdata بدل منعملها copy و قولنا هنشوف السحر اللي بتعمله std::move وقد حان الوقت و فيه ايه أحسن من اننا حرفياً نخش نشوف implementation بتاعها في الstandard library وهنلاقي الcode ده
بعيداً عن كلام templates احنا يهمنا اخر سطر اللي هنلاقي ببساطة ان هو بيعمل casting عادي من lvalue لrvalue reference بس كده هو ده كل اللي بتعمله move بتعمل casting عشان بعد كده الmove operator assignment function هي اللي يتعملها call و ديه اللي هيبقا فيها الcode اللي هينقل الdata
زي مفهمنا في مثال الvector في الأول هنلاقي ان الstandard library بتعمل حاجة شبه اللي قولناها انو يعمل pointer و يخليه يشاور علي اللي بيشاور عليه الpointer القديم ويمسح الpointer القديم و كل الكلام ده هيكون في ال Moving constructor operator في class الvector بتاع ال standard library
بس في مثال الclass Car اللي فوق ده هيشتغل تمام من غير منكتب احنا اي code و ده راجع لأن كل الtypes اللي في class Car هما unsigned int و string unique pointer و ديه كلها types متعرفة في الstandard library يعني معمولها implemenation للMove constuctor و كله تمام في الحالة ديه ممكن نخلي الCompiler هو اللي يعمل كل الLogic بتاع الMove constuctors و الحياة جميلة و المثال التالي يوضح ان الدنيا هتمشي تمام
الoutput:
الدنيا جميلة وشغالة من غير منكتب أي Logic بأيدينا بس المشكلة ممكن تيجي لو عندنا raw pointer بدل smart pointer في الClass بتاعنا في الحالة ديه لازم نكتب الLogic بأيدينا فا هيكون الكود كالأتي
عشان نفهم الcode محتاجين نفهم ان وأنا بنقل أكني عايز افضي اللي مبعوت للfunction و احطه في الobject فا هنلاقي ان في حالة الMove constructor اني هستخدم std::move في كله عشان كلها types موجود في الstandard library
بس النقطة المهمة هي اني منساش افضي pointer اللي مبعوت واخليه null pointer اما بقي في حالة الMove assignment operator محتاج أول حاجة منساش اعمل free للpointer القديم عشان ميحصلش مشكلة الdouble free
زي مقولنا في الأول خالص و بعد كده هعمل move لكل member في الclass وفي حالة الtrivial types زي الأرقام أو الpointers فا هو هيعملهم copy كده كده بس مش هيخسر انك تستخدم std::move في كله في حين ان في الحالة ديه المهم اني اعمل move للstring بس
و في الاخر برضو منساش افضي الpointer بتاع اللي مبعوتلي والصورتين دول بيوضحوا اللي هيحصل لو عملت run للcode ده
والoutput هيكون كالاتي:
الخاتمة
و بس كده المفروض نكون اخدنا فكرة عامة عن الMove Semantics و ايه هدفها و ازاي نستخدمها و اتعرفنا علي الفرق بين الlvalues و الrvalues