الذاكرة الديناميكية (new/delete)
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 14 من 104.
الآن بعد أن فهمت المؤشرات والمراجع، لنستخدمها في تخصيص الذاكرة الديناميكي. يسمح لك عاملا new و delete بإنشاء وتدمير الكائنات في الذاكرة المكدسة (heap) أثناء وقت التشغيل.
استخدم new لتخصيص الذاكرة والحصول على مؤشر إليها:
int* num = new int; // تخصيص عدد صحيح واحد
*num = 42; // تعيين قيمة من خلال المؤشر
Player* player = new Player("Hero"); // تخصيص كائنكل new يجب أن يكون له delete مطابق لتحرير الذاكرة:
delete num; // تحرير كائن واحد
delete player; // يستدعي المدمر (destructor)، ثم يحرر الذاكرةبالنسبة للمصفوفات، استخدم new[] و delete[]:
int* scores = new int[5]; // تخصيص مصفوفة من 5 أعداد صحيحة
scores[0] = 100;
delete[] scores; // تحرير المصفوفة - لاحظ الأقواس!أخطاء شائعة يجب تجنبها:
// تسرب الذاكرة - نسيان الحذف (delete)
int* ptr = new int(10);
ptr = new int(20); // فقدان الوصول إلى التخصيص الأول!
// مؤشر معلق - الاستخدام بعد الحذف
delete ptr;
*ptr = 5; // سلوك غير محدد!
// عدم تطابق الحذف (delete)
int* arr = new int[10];
delete arr; // خطأ! يجب أن يكون delete[]بعد الحذف، قم بتعيين المؤشرات إلى nullptr لتجنب إعادة الاستخدام العرضي. تعد إدارة الذاكرة اليدوية عرضة للأخطاء، ولهذا السبب تفضل لغة ++C الحديثة المؤشرات الذكية، والتي سنستكشفها لاحقاً.
التحدي
سهللنقم ببناء نظام بسيط لتخزين الرسائل يوضح تخصيص الذاكرة الديناميكي باستخدام new و delete.
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
MessageBox.h: قم بتعريف فئةMessageBoxتدير مصفوفة من الرسائل ديناميكيًا. يجب أن تحتوي الفئة على:- مؤشر خاص (private pointer) لمصفوفة من السلاسل النصية المخصصة ديناميكيًا
- متغير صحيح خاص يتتبع العدد الحالي للرسائل
- متغير صحيح خاص يخزن السعة القصوى
- منشئ (constructor) يأخذ السعة ويخصص المصفوفة باستخدام
new[] - مدمر (destructor) يحرر الذاكرة بشكل صحيح باستخدام
delete[]ويطبع"MessageBox destroyed" - دالة
addMessage(string msg)تضيف رسالة إذا كانت هناك مساحة، وتعيدtrueعند النجاح أوfalseإذا كانت ممتلئة - دالة
printAll()تطبع كل رسالة مخزنة في سطر منفصل
main.cpp: اقرأ قيمة السعة وعدد الرسائل من المدخلات. ثم اقرأ هذا العدد من السلاسل النصية للرسائل. قم بإنشاءMessageBoxعلى الـ heap باستخدامnew، وأضف جميع الرسائل إليه، واستدعِprintAll()، ثم قم بحذف MessageBox بشكل صحيح لتحرير الذاكرة.
سيكون تنسيق المدخلات كالتالي:
- السطر الأول: السعة (عدد صحيح)
- السطر الثاني: عدد الرسائل المراد إضافتها (عدد صحيح)
- الأسطر التالية: رسالة واحدة في كل سطر
تذكر: كل new تحتاج إلى delete مطابقة، وكل new[] تحتاج إلى delete[] مطابقة. بعد الحذف، من الممارسات الجيدة تعيين المؤشرات إلى nullptr.
قم بتضمين ملف الترويسة (header file) في main.cpp باستخدام #include "MessageBox.h".
ورقة مرجعية
استخدم new لتخصيص الذاكرة ديناميكيًا على الـ heap والحصول على مؤشر (pointer) إليها:
int* num = new int; // Allocate single integer
*num = 42; // Assign value through pointer
Player* player = new Player("Hero"); // Allocate objectكل عملية new يجب أن يقابلها delete مطابقة لتحرير الذاكرة:
delete num; // Free single object
delete player; // Calls destructor, then frees memoryبالنسبة للمصفوفات، استخدم new[] و delete[]:
int* scores = new int[5]; // Allocate array of 5 integers
scores[0] = 100;
delete[] scores; // Free array - note the brackets!أخطاء شائعة يجب تجنبها:
// Memory leak - forgot to delete
int* ptr = new int(10);
ptr = new int(20); // Lost access to first allocation!
// Dangling pointer - using after delete
delete ptr;
*ptr = 5; // Undefined behavior!
// Mismatched delete
int* arr = new int[10];
delete arr; // Wrong! Should be delete[]بعد الحذف، قم بتعيين المؤشرات إلى nullptr لتجنب إعادة الاستخدام عن طريق الخطأ.
جرّب بنفسك
#include <iostream>
#include <string>
#include "MessageBox.h"
using namespace std;
int main() {
int capacity;
int numMessages;
// قراءة السعة وعدد الرسائل
cin >> capacity;
cin >> numMessages;
cin.ignore(); // مسح السطر الجديد بعد الرقم
// TODO: إنشاء MessageBox على الـ heap باستخدام new
// TODO: قراءة وإضافة جميع الرسائل إلى MessageBox
for (int i = 0; i < numMessages; i++) {
string message;
getline(cin, message);
// إضافة الرسالة إلى MessageBox
}
// TODO: استدعاء printAll() لعرض جميع الرسائل
// TODO: حذف MessageBox لتحرير الذاكرة
// تذكر تعيين المؤشر إلى nullptr بعد الحذف
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةبناء وتجميع (Compilation) لغة C++ملفات الـ Header وملفات الـ Sourceالـ Namespaces والـ Scopeمقدمة في الـ OOP في C++الـ Classes مقابل الـ Objectsالمؤشر 'this'الـ Methods (الدوال الأعضاء)الـ Attributes (بيانات الأعضاء)أساسيات الـ Ctors والـ Dtorsمراجعة - آلة حاسبة بسيطة4خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticدوال الـ Getters والـ Settersدوال الأعضاء الثابتة (Const)الكلمة المفتاحية Mutableالدوال والمتغيرات الساكنة (Static)الدوال والفئات الصديقة (Friend)مراجعة - مدير الحساب البنكي7الوراثة
الوراثة الأساسيةمستويات الوصول في الوراثةترتيب استدعاء الـ Ctor والـ Dtorإعادة تعريف الدوال (Method Overriding)الدوال الافتراضية والـ VTableالوراثة المتعددةالوراثة الافتراضيةمراجعة - هيكلية الموظفين10نظرة عامة على STL
نظرة عامة وفلسفة STLحاويات STLالـ Iteratorsخوارزميات STLالـ Functors وتعبيرات Lambdaمراجعة - تكرار الكلمات13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factory و Abstract Factoryنمط Builderنمط Observerنمط Strategy2إدارة الذاكرة
ذاكرة Stack مقابل Heapالمؤشرات والمراجعالذاكرة الديناميكية (new/delete)المؤشرات الذكية في C++RAII في C++مراجعة - مدير المصفوفات الديناميكية5التغليف (Encapsulation)
محددات الوصول في C++محددات الوصول بشكل متعمقإخفاء المعلوماتStruct مقابل Classالأصناف المتداخلة والداخليةمراجعة - نظام سجلات الطلاب8تعدد الأشكال (Polymorphism)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالتحميل الزائد للدوال (Function Overloading)مراجعة الدوال الافتراضية (Virtual Functions)الدوال الافتراضية البحتة (Pure Virtual Functions)الأصناف المجردة (Abstract Classes)تصميم الواجهات (Interface) في C++التحويل الديناميكي (Dynamic Casting) و RTTIملخص - حاسبة الأشكال11مفاهيم OOP المتقدمة
التركيب مقابل الوراثةالـ Mixins عبر CRTPنمط Pimplمحو النوع (Type Erasure)أصناف Enum والأنواع القويةمعالجة الاستثناءات في OOPتسلسلات الاستثناءات المخصصة14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط قالب الطريقةنمط الحالةنمط التركيبRAII كنمط3المنشئات والموادم
المنشئ الافتراضيالمنشئ ذو المعاملاتمنشئ النسخمنشئ النقلقوائم تهيئة المنشئالمنشئات المفوضةتعمق في الموادمقاعدة الثلاثة / الخمسة / الصفرمراجعة - فئة String6تحميل العوامل (Operator Overloading)
مقدمة في تحميل العواملتحميل العوامل الحسابيةتحميل عوامل المقارنةعوامل Streamتحميل عامل التعيينتحميل العوامل [] و ()عوامل تحويل النوعمراجعة - Matrix Class9القوالب
قوالب الدوالقوالب الأصنافتخصيص القوالبالقوالب المتغيرةأساسيات SFINAE و Type Traitsمراجعة - الحاويات العامة