فصل ۱۰: حافظه پویا (Dynamic Memory) در C++

""

۱. مقدمه: حافظه در C++

هر برنامه C++ که روی سیستم شما اجرا می‌شود، از بخش‌های مختلف حافظه استفاده می‌کند. سه بخش مهم از این حافظه عبارت‌اند از:

  1. Stack (پشته) – برای ذخیره متغیرهای محلی.
  2. Heap (هیپ) – حافظه‌ی پویا که ما خودمان مدیریت می‌کنیم.
  3. Static/Data Segment – برای متغیرهای سراسری (global) یا استاتیک.

در حالت معمولی وقتی یک متغیر داخل تابع ایجاد می‌کنیم:

void func() {
    int x = 10; // حافظه روی Stack
}

این متغیر وقتی تابع تمام شود، اتوماتیک از بین می‌رود.
اما گاهی نیاز داریم حافظه‌ای ایجاد کنیم که:

  • اندازه‌اش در زمان کامپایل مشخص نیست.
  • تا زمانی که خودمان بخواهیم باقی بماند.
    اینجاست که حافظه پویا وارد عمل می‌شود.

۲. حافظه پویا چیست؟

به فضایی از Heap گفته می‌شود که در هنگام اجرای برنامه (Run Time) توسط ما ایجاد و آزاد می‌شود.
در C++ از دو عملگر مهم برای این کار استفاده می‌کنیم:

  • new → گرفتن (Allocate) حافظه
  • delete → آزاد کردن (Deallocate) حافظه

۳. استفاده از new برای متغیر تکی

مثال:

#include <iostream>
using namespace std;

int main() {
    int *p = new int; // گرفتن حافظه برای یک int
    *p = 42;
    cout << "Value: " << *p << endl;

    delete p; // آزاد کردن حافظه
    p = nullptr; // اشاره‌گر امن
}

توضیح:

  • new int → حافظه کافی برای یک عدد صحیح می‌گیرد و آدرسش را برمی‌گرداند.
  • delete p → حافظه این آدرس را آزاد می‌کند.
  • p = nullptr → اشاره‌گر را خالی می‌کنیم تا به آدرس نامعتبر اشاره نکند.

۴. استفاده از new برای آرایه

int *arr = new int[5]; // گرفتن حافظه برای 5 عدد صحیح

for (int i = 0; i < 5; i++)
    arr[i] = i + 1;

for (int i = 0; i < 5; i++)
    cout << arr[i] << " ";

delete[] arr; // آزاد کردن کل آرایه
arr = nullptr;

نکته: برای آرایه‌ها باید از delete[] استفاده کنیم، نه delete تکی.


۵. حافظه پویا برای انواع دیگر

double *pd = new double;
*pd = 3.14;

char *name = new char[20]; // رشته سبک C

۶. چرا حافظه پویا مهم است؟

  • زمانی که اندازه داده از پیش معلوم نیست.
  • کار با داده‌های بزرگ که نمی‌خواهیم در Stack ذخیره شوند.
  • ایجاد ساختارهای داده دینامیک (مثل لیست پیوندی، درخت، گراف).

۷. مشکلات و خطرات حافظه پویا

۷.۱. Memory Leak (نشت حافظه)

وقتی حافظه‌ای با new گرفته ولی با delete آزاد نشود.

int *p = new int(10);
// هیچ delete p وجود ندارد → حافظه در اختیار سیستم برنمی‌گردد

۷.۲. Dangling Pointer

وقتی اشاره‌گر به حافظه‌ای اشاره کند که آزاد شده است.

int *p = new int(5);
delete p;
cout << *p; // خطرناک

۷.۳. Double Delete

اگر یک آدرس را دو بار آزاد کنیم، ممکن است برنامه کرش کند.

delete p;
delete p; // خطرناک

۸. مدیریت درست حافظه

  • همیشه بعد از delete، اشاره‌گر را به nullptr تنظیم کنید.
  • به ازای هر new باید یک delete (و برای هر new[] یک delete[]) وجود داشته باشد.
  • از اشاره‌گرهای هوشمند (در فصل ۱۱ می‌آید) برای مدیریت اتوماتیک حافظه استفاده کنید.

۹. مثال عملی: آرایه با اندازه ورودی کاربر

#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "تعداد عناصر: ";
    cin >> n;

    int *arr = new int[n];

    for (int i = 0; i < n; i++) {
        cout << "عنصر " << i+1 << ": ";
        cin >> arr[i];
    }

    cout << "آرایه شما: ";
    for (int i = 0; i < n; i++)
        cout << arr[i] << " ";

    delete[] arr;
    arr = nullptr;
}

۱۰. مثال: ماتریس پویا (۲ بعدی)

int rows, cols;
cin >> rows >> cols;

int **matrix = new int*[rows];
for (int i = 0; i < rows; i++)
    matrix[i] = new int[cols];

// استفاده...
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        matrix[i][j] = i + j;
    }
}

// نمایش
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        cout << matrix[i][j] << " ";
    }
    cout << endl;
}

// آزاد کردن
for (int i = 0; i < rows; i++)
    delete[] matrix[i];
delete[] matrix;
matrix = nullptr;

۱۱. نکات پیشرفته

  • می‌توانید هنگام new مقدار اولیه دهید:
int *p = new int(100); // مقدار اولیه 100
  • استفاده از عملگر nothrow برای جلوگیری از پرتاب استثنا:
int *p = new(nothrow) int[1000];
if (!p) {
    cout << "حافظه کافی نیست";
}
  • برای اشیاء کلاس‌ها، new سازنده (constructor) را صدا می‌زند، و delete مخرب (destructor) را.

۱۲. تمرین‌ها

۱. برنامه‌ای که یک آرایه با اندازه دلخواه از کاربر بگیرد، میانگین عناصر را محاسبه کند.
۲. برنامه‌ای که یک ماتریس پویا بسازد و ترانهاده (Transpose) آن را چاپ کند.
۳. برنامه‌ای که اندازه آرایه را کاربر بدهد، مقادیر تصادفی در آن بریزد و بزرگ‌ترین مقدار را پیدا کند.
۴. بررسی کنید اگر new حافظه نداد، پیغام مناسب چاپ شود.


۱۳. جمع‌بندی

  • حافظه پویا برای داده‌هایی با اندازه متغیر و طول عمر قابل کنترل استفاده می‌شود.
  • new و delete ابزارهای اصلی مدیریت در C++ سنتی هستند.
  • مدیریت اشتباه باعث نشتی حافظه، کرش یا باگ‌های مخفی می‌شود.
  • در فصل بعد یاد می‌گیریم چگونه با اشاره‌گرهای هوشمند این مدیریت را اتوماتیک کنیم.
محمد وب‌سایت

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

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