std::move() في C++: ما هو وكيفية استخدامه في 2025

مدرب بايثون أونلاين للمبتدئين

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

ابدأ الدورة

تعرف على: std::move() — ليس كما يبدو

\n\n

تخيل موقفًا: لقد كتبت class يدير مصفوفة كبيرة من البيانات. كل شيء يعمل، ولكن مع كل نسخ للكائن يحدث تكرار كامل لميغابايتات من المعلومات. هذا بطيء ومُبذّر. ما الحل؟ في C++11 ظهر حل أنيق — دلالات النقل وبطلها الرئيسي std::move().

\n\n

في عام 2025، عندما تكون الأداء وإدارة الذاكرة الفعالة أمرًا بالغ الأهمية، يصبح فهم std::move() مهارة إلزامية لكل مطور C++. لكن المفارقة: على الرغم من الاسم، std::move() لا ينقل أي شيء. إنه فقط يجعل الكائن «جاهزًا للانتقال».

\n\n

في هذه المقالة، سنشرح ما يفعله std::move() بالفعل، ومتى يجب استخدامه، ومتى من الأفضل الامتناع. استعد — سيكون هناك الكثير من أمثلة الكود ولا نظرية مملة.

\n\n

ما هو std::move() في الواقع؟

\n\n

دعنا نبدد الأسطورة الرئيسية فورًا. std::move() ليست دالة تنقل البيانات فعليًا من كائن إلى آخر. إنه تحويل نوع (cast) يحول lvalue إلى rvalue-reference.

\n\n

إليك تنفيذها المبسط (لا تقلق، إنه بسيط):

\n\n
template<typename T>\ntypename std::remove_reference<T>::type&& move(T&& arg) noexcept {\n    return static_cast<typename std::remove_reference<T>::type&&>(arg);\n}\n
\n\n

كل ما يفعله std::move() هو أخذ الكائن الخاص بك ويقول للمترجم: «انظر، هذا كائن مؤقت، يمكنك سرقته». ومن ثم تقوم المنشئات وعوامل الإسناد ذات دلالات النقل بعملها.

\n\n

lvalue مقابل rvalue: شرح سريع

\n\n

لفهم std::move()، يجب التمييز بين نوعين من التعبيرات:

\n\n
    \n
  • lvalue (قيمة يسارية) — كائن له اسم وعنوان في الذاكرة. مثال: int x = 5; — x هو lvalue.
  • \n
  • rvalue (قيمة يمنى) — كائن مؤقت «يعيش» فقط في التعبير الحالي. مثال: 5، نتيجة دالة getTemp()، أو x + 3.
  • \n
\n\n

تعمل دلالات النقل مع rvalue: إذا كان الكائن مؤقتًا، فلماذا ننسخه؟ يمكننا ببساطة «سرقة» موارده (مؤشرات، مخازن مؤقتة، واصفات ملفات) وترك الكائن الأصلي في حالة «فارغة».

\n\n

متى تستخدم std::move(): سيناريوهات عملية

\n\n

الآن بعد أن عرفنا النظرية، دعنا نستعرض الحالات الحقيقية التي يكون فيها std::move() مفيدًا.

\n\n

1. تمرير الكائنات الكبيرة إلى الدوال

\n\n

تخيل أن لديك class BigData يحتوي على مصفوفة من مليون عنصر. تمريرها بالقيمة دون نقل هو انتحار:

\n\n
#include <iostream>\n#include <vector>\n#include <string>\n\nclass BigData {\n    std::vector<int> data;\npublic:\n    BigData(size_t size) : data(size) {}\n    \n    // منشئ النقل\n    BigData(BigData&& other) noexcept : data(std::move(other.data)) {\n        std::cout << "Move constructor called\\n";\n    }\n    \n    // منشئ النسخ\n    BigData(const BigData& other) : data(other.data) {\n        std::cout << "Copy constructor called\\n";\n    }\n};\n\nvoid processData(BigData obj) {\n    // العمل مع البيانات\n}\n\nint main() {\n    BigData big(1000000);\n    processData(std::move(big)); // سيتم استدعاء منشئ النقل\n    // بعد ذلك big في حالة "فارغة"\n    return 0;\n}\n
\n\n

بدون std::move()، كان سيتم استدعاء منشئ النسخ مع تكرار كامل للمليون عنصر. مع النقل — فقط ننقل المؤشرات. الفرق — أسرع بآلاف المرات.

\n\n

2. إرجاع الكائنات الثقيلة من الدوال

المدونات

توصيات الكتب