در این مقاله با مفهوم تابع مجازی در ++C و موارد استفاده آن آشنا میشویم. همچنین با تابع مجازی خالص و کلاس مجرد نیز آشنا خواهیم شد. تابع مجازی یک تابع عضو در کلاس مبنا است که انتظار میرود در کلاسهای مشتق شده بازتعریف شود. پیش از آن که وارد جزییات شویم، ابتدا تلاش میکنیم به صورت شهودی دلیل نیاز به تابعهای مجازیهای را بفهمیم. برای مطالعه بخش قبلی این مقالات روی لینک زیر کلیک کنید:
یک مثال برای شروع
فرض کنید روی یک بازی (جنگی) کار میکنیم. کلاس Weapon را ایجاد میکنیم و دو کلاس Bomb و Gun را از آن مشتق میکنیم تا ویژگیهای این سلاحها را در آنها بارگذاری کنیم:
#include <iostream> using namespace std; class Weapon { public: void loadFeatures() { cout << "Loading weapon features.\n"; } }; class Bomb : public Weapon { public: void loadFeatures() { cout << "Loading bomb features.\n"; } }; class Gun : public Weapon { public: void loadFeatures() { cout << "Loading gun features.\n"; } }; int main() { Weapon *w = new Weapon; Bomb *b = new Bomb; Gun *g = new Gun; w->loadFeatures(); b->loadFeatures(); g->loadFeatures(); return 0; }
خروجی
Loading weapon features. Loading bomb features. Loading gun features.
سه شیء اشارهگر به نامهای w ،b و g را به ترتیب برای کلاسهای Weapon ،Bomb و Gun تعریف میکنیم. همچنین تابع عضو ()loadFeatures را برای هر شیء با استفاده از کدهای زیر فراخوانی میکنیم:
w->loadFeatures(); b->loadFeatures(); g->loadFeatures();
تا این جا همه چیز به خوبی کار میکند، با این حال پروژه بازی ما رفتهرفته بزرگتر میشود و تصمیم میگیریم کلاس مجزای Loader را برای بارگذاری ویژگیهای سلاحها ایجاد کنیم. این کلاس Loader ویژگیهای دیگری از سلاحها را بسته به نوع سلاح انتخابی بارگذاری میکند.
class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } };
()loadFeatures ویژگی یک سلاح خاص را بارگذاری میکند.
پیادهسازی کلاس Loader
#include <iostream> using namespace std; class Weapon { public: Weapon() { cout << "Loading weapon features.\n"; } void features() { cout << "Loading weapon features.\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "Loading bomb features.\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "Loading gun features.\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
خروجی
Loading weapon features. Loading weapon features. Loading weapon features. Loading weapon features.
پیادهسازی ما درست به نظر میرسد؛ اما ویژگیهای سلاحها 4 بار بارگذاری شده است. دلیل این مسئله آن است که در ابتدا شیء سلاح w به شیء کلاس b (یا Bomb) اشاره میکرد و تلاش کردهایم ویژگیهای شیء Bomb را با ارسال آن به تابع ()loadFeatures با استفاده از شیء اشارهگر l (کلاس Loader) بارگذاری کنیم. به طور مشابه تلاش کردهایم ویژگیهای شیء Gun را نیز بارگذاری کنیم، اما تابع ()loadFeatures کلاس Loader اشارهگر به شیء کلاس Weapon را به عنوان آرگومان میگیرد:
void loadFeatures(Weapon *weapon)
به همین جهت است که ویژگیهای weapon 4 بار بارگذاری میشوند. برای حل این مشکل باید تابعی از کلاس مبنا (کلاس Weapon) به صورت مجازی با استفاده از کلیدواژه virtual بسازیم.
class Weapon { public: virtual void features() { cout << "Loading weapon features.\n"; } };
مثال: استفاده از تابع مجازی برای حل مشکل
#include <iostream> using namespace std; class Weapon { public: virtual void features() { cout << "Loading weapon features.\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "Loading bomb features.\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "Loading gun features.\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
خروجی
Loading weapon features. Loading bomb features. Loading weapon features. Loading gun features.
همچنین توجه کنید که تابع l->loadFeatures(w) تابع کلاسهای مختلفی را بسته به این که شیء l به چه چیزی اشاره میکند فرامیخواند.
استفاده از تابع مجازی نهتنها موجب شده کد ما روشنتر شود، بلکه انعطافپذیری آن را نیز افزایش داده است. در برنامه فوق، ویژگیهای سلاح دو بار پرینت شدهاند. با افزودن کد بیشتر میتوانید کاری کنید که ویژگیهای سلاح تنها یک بار بارگذاری شود. اگر بخواهیم سلاح دیگری (مانند چاقو) اضافه میکنیم میتوانیم به سادگی ویژگیهای آن را به صورت زیر اضافه کرده و بارگذاری کنیم.
class Knife : public Weapon { public: void features() { this->Weapon::features(); cout << "Loading knife features.\n"; } };
و در تابع ()main به صورت زیر عمل میکنیم:
Knife k; w = &k; l->loadFeatures(w);
لازم به ذکر است که ما برای بارگذاری ویژگیهای چاقو، هیچ چیز را در کلاس Loader تغییر ندادهایم.
کلاس مجرد در ++C و تابع مجازی خالص
هدف برنامهنویسی شیءگرا، تقسیم کردن یک مسئله پیچیده به مجموعههای کوچکتر است. بدین ترتیب میتوانیم مسئله را به روشی بهینه درک کرده و حل کنیم.
برخی اوقات استفاده از وراثت صرفاً به منظور بصریسازی بهتر مسئله مفید خواهد بود. در ++C میتوان یک کلاس مجرد ایجاد کرد که امکان وهلهسازی از آن وجود نداشته باشد، یعنی نمیتوان شیئی از روی این کلاس مجرد ساخت. با این حال میتوان از آن کلاس دیگری مشتق کرد و شیئی را از آن کلاس مشتق شده وهلهسازی نمود.
کلاسهای مجرد کلاسهای مبنایی هستند که وهلهسازی نمیشوند. یک کلاس شامل تابع مجازی خالص به نام کلاس مجرد نامیده میشود.
تابع مجازی خالص
یک تابع مجازی که اعلان آن با =0 پایان یابد یک تابع مجازی خالص است. به مثال زیر توجه کنید:
class Weapon { public: virtual void features() = 0; };
در این کد تابع مجازی به صورت زیر است:
virtual void features() = 0
و کلاس Weapon یک کلاس مجرد است.
مثال: کلاس مجرد و تابع مجازی خالص
#include <iostream> using namespace std; // Abstract class class Shape { protected: float l; public: void getData() { cin >> l; } // virtual Function virtual float calculateArea() = 0; }; class Square : public Shape { public: float calculateArea() { return l*l; } }; class Circle : public Shape { public: float calculateArea() { return 3.14*l*l; } }; int main() { Square s; Circle c; cout << "Enter length to calculate the area of a square: "; s.getData(); cout<<"Area of square: " << s.calculateArea(); cout<<"\nEnter radius to calculate the area of a circle: "; c.getData(); cout << "Area of circle: " << c.calculateArea(); return 0; }
خروجی
Enter length to calculate the area of a square: 4 Area of square: 16 Enter radius to calculate the area of a circle: 5 Area of circle: 78.5
در این برنامه، تابع مجازی خالص زیر درون کلاس Shape تعریف شده است:
virtual float area() = 0;
یک نکته که باید اشاره کنیم این است که تابع مجازی خالص کلاس مبنا باید در کلاس مشتق شده باطل (Override) شود. اگر این کار صورت نگیرد، کلاس مشتق شده خود به یک کلاس مجرد تبدیل میشود. برای مطالعه بخش بعدی روی لینک زیر کلیک کنید:
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامه نویسی C++
- مجموعه آموزشهای دروس علوم و مهندسی کامپیوتر
- کپسولهسازی در ++C و C — به زبان ساده
- مبانی ++C برای یادگیری ساختمان داده — به زبان ساده
==
چهارتا ویژگی بارگذاری میکنه چون بدون مجازی با سازنده نوشتین دلیل و فرقش یه چیز دیگس