عملية الـ Upcasting
جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 31 من 61.
التحويل التصاعدي (Upcasting) هو عملية معاملة نوع محدد (مشتق) كنوعه الأكثر عمومية (أساسي). في نموذج التركيب (composition model) الخاص بلغة C، يعني هذا تمرير مؤشر (pointer) إلى struct يدمج نوعاً أساسياً إلى دالة تتوقع النوع الأساسي.
ضع في اعتبارك الهيكل الأساسي Vehicle و Car الذي يحتويه:
typedef struct {
int speed;
} Vehicle;
typedef struct {
Vehicle vehicle; // الأساس كعضو أول
int doors;
} Car;تخيل الآن دالة عامة تعمل مع أي مركبة:
void accelerate(Vehicle* v, int amount) {
v->speed += amount;
}بفضل قاعدة العضو الأول، يمكننا تمرير Car* إلى هذه الدالة عن طريق تحويله إلى Vehicle*:
Car my_car = {{0}, 4}; // speed=0, doors=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed); // Output: 50هذا هو التحويل للأعلى (upcasting) — نحن ننتقل "للأعلى" من نوع محدد إلى نوع عام. الدالة accelerate لا تعرف أنها تعمل مع Car؛ بل ترى فقط Vehicle. يتيح لك ذلك كتابة كود قابل لإعادة الاستخدام يعمل على النوع الأساسي مع قبول أي نوع مشتق يقوم بتضمينه.
التحدي
سهللنقم ببناء نظام نقل بسيط يوضح عملية الـ upcasting (التحويل التصاعدي) بشكل عملي. ستقوم بإنشاء نوع أساسي Vehicle ونوع أكثر تخصصاً وهو Motorcycle، ثم توضح كيف يمكن لدالة عامة أن تعمل مع كليهما من خلال تحويل المؤشرات (pointer casting).
تكمن قوة الـ upcasting في أنه يمكنك كتابة دوال للنوع الأساسي، وستعمل بسلاسة مع أي نوع مشتق يتضمنه. فالدراجة النارية "هي" مركبة، لذا فإن أي دالة تتوقع Vehicle* يجب أن تكون قادرة على معالجتها.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
vehicle.h: قم بتعريف هيكل الـ structs الخاص بك مع استخدام حواجز التضمين (include guards). أنشئ struct باسمVehicleيحتوي على حقلين:speed(عدد صحيح للسرعة الحالية) وmax_speed(عدد صحيح للسرعة القصوى للمركبة). ثم قم بتعريف struct باسمMotorcycleيدمجVehicleكعضو أول فيه ويضيف حقلhas_sidecar(عدد صحيح، حيث 1 يعني نعم و 0 يعني لا). قم بالتصريح عن دالتين:boostالتي تأخذVehicle*ومقداراً لزيادة السرعة، وprint_motorcycleالتي تأخذMotorcycle*.vehicle.c: قم بتنفيذ كلتا الدالتين. يجب أن تقوم دالةboostبزيادة سرعة المركبة بالمقدار المحدد، ولكن مع وضع حد أقصى هوmax_speed—إذا كانت السرعة الجديدة ستتجاوز الحد الأقصى، فاجعلها تساويmax_speedبدلاً من ذلك. يجب أن تعرض دالةprint_motorcycleجميع معلومات الدراجة النارية بما في ذلك بيانات المركبة المدمجة فيها.main.c: قم بإنشاءMotorcycleوتهيئ جميع حقولها. ثم وضح عملية الـ upcasting من خلال استدعاء دالةboost—قم بتحويلMotorcycle*إلىVehicle*لتمريره إلى هذه الدالة العامة. بعد عملية التعزيز (boosting)، قم بطباعة معلومات الدراجة النارية لرؤية النتيجة.
ستتلقى أربعة مدخلات: السرعة الابتدائية للدراجة النارية، سرعتها القصوى، ما إذا كانت تحتوي على عربة جانبية (1 أو 0)، و مقدار التعزيز (boost amount).
يجب أن يبدو مخرجك كالتالي:
Speed: 75
Max Speed: 120
Sidecar: Yesحيث 75 هي السرعة بعد التعزيز (الابتدائية 50 + التعزيز 25)، و 120 هي السرعة القصوى، و Yes أو No تشير إلى ما إذا كانت الدراجة النارية تحتوي على عربة جانبية. إذا كان التعزيز سيدفع السرعة لتتجاوز max_speed، فيجب أن تقتصر السرعة على max_speed.
الدرس الأساسي هنا هو أن boost لا تعرف شيئاً عن الدراجات النارية—فهي تعمل فقط مع Vehicle. ومع ذلك، من خلال الـ upcasting، يمكن لدراجتك النارية استخدام هذه الدالة العامة لتعديل بيانات المركبة المدمجة فيها.
ورقة مرجعية
يعامل الـ Upcasting النوع المحدد (المشتق) كنوعه الأكثر عمومية (الأساسي) عن طريق تمرير مؤشر (pointer) إلى هيكل (struct) يتضمن نوعاً أساسياً إلى دالة تتوقع النوع الأساسي.
قم بتعريف هيكل أساسي (base struct) وهيكل مشتق (derived struct) يتضمنه كعضو أول:
typedef struct {
int speed;
} Vehicle;
typedef struct {
Vehicle vehicle; // الأساس كعضو أول
int doors;
} Car;أنشئ دالة تعمل مع النوع الأساسي:
void accelerate(Vehicle* v, int amount) {
v->speed += amount;
}قم بتحويل مؤشر النوع المشتق إلى مؤشر النوع الأساسي لاستدعاء الدالة:
Car my_car = {{0}, 4}; // speed=0, doors=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed); // المخرجات: 50يتيح لك ذلك كتابة كود قابل لإعادة الاستخدام يعمل على النوع الأساسي مع قبول أي نوع مشتق يتضمنه.
جرّب بنفسك
#include <stdio.h>
#include "vehicle.h"
int main() {
int initial_speed, max_speed, has_sidecar, boost_amount;
// قراءة المدخلات
scanf("%d", &initial_speed);
scanf("%d", &max_speed);
scanf("%d", &has_sidecar);
scanf("%d", &boost_amount);
// TODO: إنشاء Motorcycle وتهيئة جميع حقولها
// تذكر: Motorcycle تتضمن Vehicle كعضو أول لها
// TODO: توضيح الـ upcasting من خلال استدعاء boost
// قم بتحويل Motorcycle* إلى Vehicle* لتمريره إلى دالة boost
// TODO: طباعة معلومات الـ motorcycle
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات البرمجة التركيبية
ملفات الترويسةحواجز التضمينملفات المصدرالدوال الساكنةمراجعة: آلة حاسبة تركيبية4التغليف (Encapsulation)
مفهوم الـ Opaque Pointersتعريف الـ Opaque Structsالـ Getters والـ Settersالتحقق من البيانات في الـ Settersملخص: الصندوق السري2الكائنات والأساليب
الـ Structs ككائناتمؤشر 'Self'صحة استخدام Constالمؤشر مقابل القيمةالأساليب المساعدةملخص: Point Manager5مشروع: حساب بنكي بسيط
إعداد المشروعتنفيذ الحساب3دورة حياة الكائن
نمط المنشئ (Constructor Pattern)نمط الهادم (Destructor Pattern)تهيئة الـ Stackالنسخ العميق (Deep Copy)مراجعة: String Wrapper6الوراثة عبر التركيب
تضمين الـ Structقاعدة العضو الأولالوصول إلى أعضاء الأبعملية الـ Upcastingمراجعة: هرمية الأشكال9مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرة