فصل ۶: محدوده و طول عمر متغیرها در C++

""

🎯 هدف یادگیری

در پایان این فصل شما می‌توانید:

  1. تفاوت محدوده (Scope) و طول عمر (Lifetime) متغیرها را درک کنید.
  2. انواع محدوده (محلی، بلوکی، سراسری) را بشناسید.
  3. رفتار Shadowing (پوشاندن متغیر) را تشخیص دهید و از مشکلاتش جلوگیری کنید.
  4. تفاوت متغیرهای static، global و local را بفهمید.
  5. نحوه صحیح استفاده از متغیرها بر اساس محدوده و طول عمرشان را یاد بگیرید.

۱. مقدمه

هر متغیری که در C++ تعریف می‌کنیم یک دامنه معتبر (Scope) و یک مدت زمان زنده بودن (Lifetime) دارد.

  • Scope = کجا قابل دسترسی است.
  • Lifetime = کی ساخته می‌شود و کی از بین می‌رود.

بی‌توجهی به این مفاهیم می‌تواند باعث ایجاد باگ‌های بسیار سخت شود.


۲. انواع محدوده متغیرها (Scope)

۲.۱ محدوده بلوکی (Block Scope)

  • متغیری که داخل {} تعریف می‌شود فقط همانجا معتبر است.
  • بعد از خروج از بلاک، متغیر حذف می‌شود.
#include <iostream>
using namespace std;

int main() {
    int x = 10;
    {
        int y = 5;
        cout << "x = " << x << ", y = " << y << endl;
    }
    // cout << y; // ❌ خطا: y خارج از دسترس است.
}

۲.۲ محدوده تابعی (Local Scope)

  • متغیر داخل یک تابع فقط همان تابع می‌تواند بخواند/تغییر دهد.
  • با هر بار فراخوانی تابع، متغیر محلی دوباره ساخته و مقداردهی می‌شود.
void test() {
    int a = 0;
    a++;
    cout << a << endl;
}

int main() {
    test(); // خروجی: 1
    test(); // خروجی: 1 (هر بار a از صفر شروع می‌کند)
}

۲.۳ محدوده سراسری (Global Scope)

  • متغیر خارج از همه توابع تعریف شود و همه جای فایل قابل دسترسی باشد (بعد از محل تعریف).
  • اگر در چند فایل استفاده شود باید extern استفاده شود.
#include <iostream>
using namespace std;

int counter = 0; // متغیر سراسری

void inc() {
    counter++;
}

int main() {
    inc();
    inc();
    cout << counter; // خروجی: 2
}

⚠ استفاده بیش از حد از متغیرهای global می‌تواند باعث وابستگی زیاد و سختی در عیب‌یابی شود.


۳. Shadowing (پوشاندن متغیر)

اگر یک متغیر محلی هم‌نام متغیر سراسری یا متغیر بیرونی باشد، متغیر محلی متغیر بیرونی را مخفی می‌کند.

int x = 100; // global

int main() {
    int x = 50; // local → global x پنهان شد
    cout << x << endl; // 50
    cout << ::x << endl; // دسترسی به global: 100
}

📌 عملگر :: = Scope Resolution Operator کمک می‌کند به متغیر global دسترسی پیدا کنیم.


۴. طول عمر (Lifetime) متغیرها

۴.۱ Auto (پیش‌فرض)

  • هر متغیر محلی بدون static → با ورود به بلوک ساخته می‌شود، با خروج حذف می‌شود.

۴.2 Static Local

  • موقع اولین اجرای تابع مقداردهی اولیه می‌شود و تا پایان اجرای برنامه در حافظه باقی می‌ماند.
  • مقدارش حفظ می‌شود.
void counterFunc() {
    static int cnt = 0;
    cnt++;
    cout << cnt << endl;
}

int main() {
    counterFunc(); // 1
    counterFunc(); // 2
    counterFunc(); // 3
}

۴.۳ Global / Static Global

  • متغیر global: در کل برنامه زنده است.
  • متغیر static global: فقط در همان فایل دیده می‌شود، ولی تمام برنامه زنده می‌ماند.

۵. مثال عملی ترکیب Scope و Lifetime

#include <iostream>
using namespace std;

int g = 0; // global

void test() {
    int localVar = 0; // auto local
    static int staticVar = 0; // static local
    g++;
    localVar++;
    staticVar++;
    cout << "g=" << g << ", localVar=" << localVar
         << ", staticVar=" << staticVar << endl;
}

int main() {
    test();
    test();
    test();
}

خروجی:

g=1, localVar=1, staticVar=1
g=2, localVar=1, staticVar=2
g=3, localVar=1, staticVar=3

📌 دلیل:

  • g → global → مقدارش بین اجراها حفظ می‌شود.
  • localVar → auto local → هر بار تازه ساخته می‌شود.
  • staticVar → static local → فقط یک بار ساخته می‌شود و مقدارش حفظ می‌گردد.

۶. Scope Nesting (محدوده‌های تو در تو)

  • محدوده‌ها می‌توانند داخل هم باشند.
  • در محدوده داخلی، به متغیرهای محدوده خارجی (اگر همنام نباشند) دسترسی داریم.
int main() {
    int a = 5;
    {
        int b = 10;
        cout << a << ", " << b << endl; // a و b هر دو
    }
    // cout << b; // ❌ خطا
}

۷. نکات کلیدی

  • متغیرهای محلی توصیه می‌شوند چون باعث کاهش اثرات جانبی می‌شوند.
  • متغیرهای global فقط وقتی واقعاً لازم است استفاده شوند.
  • از نامگذاری دقیق برای جلوگیری از Shadowing.
  • از static برای حفظ وضعیت بین فراخوانی‌ها بدون استفاده از global.
  • متغیرهای const با هر Scope رفتار خاص خود را دارند (برای مقدار ثابت).

۸. اشتباهات رایج

❌ استفاده بیش از حد از global → وابستگی زیاد، باگ‌های پنهان.
❌ تعریف دوباره متغیر با نام مشابه → Shadowing ناخواسته.
❌ فراموش کردن این که static متغیر را یک بار مقداردهی می‌کند.
❌ اشتباه گرفتن Scope با Lifetime.


۹. تمرین‌ها

تمرین ۱

برنامه‌ای بنویسید که متغیر global و یک متغیر local هم‌نام داشته باشد و تفاوت دسترسی به آن‌ها را نشان دهد.

تمرین ۲

یک تابع بنویسید که هر بار فراخوانی شد تعداد دفعات اجرا را با استفاده از static ذخیره کند.

تمرین ۳

با استفاده از بلوک‌های تو در تو بررسی کنید که کدام متغیرها در کجا قابل دسترسی هستند.

تمرین ۴ (چالش)

یک برنامه منوی ساده بسازید که از یک متغیر static برای ذخیره تعداد دفعات انتخاب گزینه استفاده کند.


۱۰. جمع‌بندی این فصل

  • Scope = بازه مکانی دسترسی متغیر.
  • Lifetime = بازه زمانی وجود متغیر در حافظه.
  • انواع Scope: local (بلوک/تابع)، global.
  • انواع Lifetime: auto (پیش‌فرض)، static local، global/static global.
  • استفاده صحیح از متغیرها بر اساس این مفاهیم به کاهش باگ و بهینه‌سازی کمک می‌کند.

محمد وب‌سایت

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *