المراجع وتمرير المعاملات (References) — أساسيات C++
المراجع (References) وتمرير المعاملات
في الدرس السابق رأيت أن الدالة تستلم نسخة من المعامل. أحياناً نريد عكس ذلك: أن تعدّل الدالة المتغير الأصلي، أو أن نتجنب نسخ بيانات كبيرة. الأداة لذلك في C++ هي المرجع.
ما هو المرجع؟
المرجع اسم آخر لنفس المتغير (alias):
int score = 80;
int& ref = score; // ref اسم آخر لـ score
ref = 95;
cout << score; // 95- يُعلن بـ & بعد النوع.
- يجب أن يُربط بمتغير عند إنشائه، ولا يمكن إعادة ربطه بمتغير آخر لاحقاً.
التمرير بالمرجع: تعديل الأصل
void addBonus(int& score, int bonus) {
score += bonus; // يعدّل متغير المستدعي
}
int s = 70;
addBonus(s, 10); // s = 80مثال كلاسيكي: تبديل قيمتين void swapValues(int& a, int& b). (المكتبة فيها std::swap جاهزة.)
const&: قراءة بدون نسخ
نسخ نص طويل أو vector فيها مليون عنصر عند كل استدعاء مكلف. التمرير بـ const& يعطي الدالة وصولاً للأصل للقراءة فقط:
double average(const vector<int>& numbers) {
// numbers.push_back(1); // خطأ ترجمة: const
if (numbers.empty()) return 0;
double sum = 0;
for (int n : numbers) sum += n;
return sum / numbers.size();
}القاعدة العملية
| الحالة | الطريقة |
|---|---|
| أنواع صغيرة (int، double، char، bool) للقراءة | بالقيمة: int x |
| أنواع كبيرة (string، vector، كائنات) للقراءة | const T& |
| الدالة يجب أن تعدّل الأصل | T& |
إرجاع عدة نتائج
بدلاً من المعاملات المرجعية "للإخراج"، الأوضح غالباً إرجاع قيمة مركبة، مثل std::pair أو struct (ستتعلمها لاحقاً). استخدم المراجع للإخراج عند الحاجة فقط.
⚠️ لا ترجع مرجعاً لمتغير محلي
int& bad() {
int local = 5;
return local; // local يُدمَّر عند انتهاء الدالة ← مرجع معلّق
}وماذا عن المؤشرات؟
المؤشرات (int*) أداة أخرى تخزن عنوان المتغير، ويمكن أن تكون فارغة أو تتغير. ستتعلمها بالتفصيل في دورة "المؤشرات وإدارة الذاكرة". في الكود الحديث، المراجع هي الخيار الأول لتمرير المعاملات.
تمرين
اكتب void normalize(vector<double>& values) تقسم كل عنصر على أكبر قيمة، و int countAbove(const vector<int>& v, int limit) دون نسخ.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void tryChange(int x) { // بالقيمة: نسخة
x = 100;
}
void addBonus(int& score, int bonus) { // بالمرجع: يعدّل الأصل
score += bonus;
}
void swapValues(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// const& : قراءة بدون نسخ
double average(const vector<int>& numbers) {
if (numbers.empty()) return 0;
int sum = 0;
for (int n : numbers) sum += n;
return static_cast<double>(sum) / numbers.size();
}
void shout(const string& text) {
cout << text << "!" << endl;
}
int main() {
int score = 70;
tryChange(score);
cout << "بعد tryChange: " << score << endl; // 70
addBonus(score, 10);
cout << "بعد addBonus: " << score << endl; // 80
int a = 1, b = 2;
swapValues(a, b);
cout << "a=" << a << " b=" << b << endl; // a=2 b=1
int& alias = score; // اسم آخر لنفس المتغير
alias = 95;
cout << "score الآن: " << score << endl; // 95
vector<int> grades = {90, 75, 88};
cout << "المتوسط: " << average(grades) << endl;
shout("C++ ممتعة");
return 0;
}
اختبر فهمك