تابع مجازی در ++C — به زبان ساده
در این مقاله با مفهوم تابع مجازی در ++C و موارد استفاده آن آشنا میشویم. همچنین با تابع مجازی خالص و کلاس مجرد نیز آشنا خواهیم شد. تابع مجازی یک تابع عضو در کلاس مبنا است که انتظار میرود در کلاسهای مشتق شده بازتعریف شود. پیش از آن که وارد جزییات شویم، ابتدا تلاش میکنیم به صورت شهودی دلیل نیاز به تابعهای مجازیهای را بفهمیم. برای مطالعه بخش قبلی این مقالات روی لینک زیر کلیک کنید:
یک مثال برای شروع
فرض کنید روی یک بازی (جنگی) کار میکنیم. کلاس Weapon را ایجاد میکنیم و دو کلاس Bomb و Gun را از آن مشتق میکنیم تا ویژگیهای این سلاحها را در آنها بارگذاری کنیم:
1#include <iostream>
2using namespace std;
3class Weapon
4{
5 public:
6 void loadFeatures()
7 { cout << "Loading weapon features.\n"; }
8};
9class Bomb : public Weapon
10{
11 public:
12 void loadFeatures()
13 { cout << "Loading bomb features.\n"; }
14};
15class Gun : public Weapon
16{
17 public:
18 void loadFeatures()
19 { cout << "Loading gun features.\n"; }
20};
21int main()
22{
23 Weapon *w = new Weapon;
24 Bomb *b = new Bomb;
25 Gun *g = new Gun;
26 w->loadFeatures();
27 b->loadFeatures();
28 g->loadFeatures();
29 return 0;
30}
خروجی
Loading weapon features. Loading bomb features. Loading gun features.
سه شیء اشارهگر به نامهای w ،b و g را به ترتیب برای کلاسهای Weapon ،Bomb و Gun تعریف میکنیم. همچنین تابع عضو ()loadFeatures را برای هر شیء با استفاده از کدهای زیر فراخوانی میکنیم:
w->loadFeatures(); b->loadFeatures(); g->loadFeatures();
تا این جا همه چیز به خوبی کار میکند، با این حال پروژه بازی ما رفتهرفته بزرگتر میشود و تصمیم میگیریم کلاس مجزای Loader را برای بارگذاری ویژگیهای سلاحها ایجاد کنیم. این کلاس Loader ویژگیهای دیگری از سلاحها را بسته به نوع سلاح انتخابی بارگذاری میکند.
1class Loader
2{
3 public:
4 void loadFeatures(Weapon *weapon)
5 {
6 weapon->features();
7 }
8};
()loadFeatures ویژگی یک سلاح خاص را بارگذاری میکند.
پیادهسازی کلاس Loader
1#include <iostream>
2using namespace std;
3class Weapon
4{
5 public:
6 Weapon() { cout << "Loading weapon features.\n"; }
7
8 void features()
9 { cout << "Loading weapon features.\n"; }
10};
11class Bomb : public Weapon
12{
13 public:
14 void features()
15 {
16 this->Weapon::features();
17 cout << "Loading bomb features.\n";
18 }
19};
20class Gun : public Weapon
21{
22 public:
23 void features()
24 {
25 this->Weapon::features();
26 cout << "Loading gun features.\n";
27 }
28};
29class Loader
30{
31 public:
32 void loadFeatures(Weapon *weapon)
33 {
34 weapon->features();
35 }
36};
37int main()
38{
39 Loader *l = new Loader;
40 Weapon *w;
41 Bomb b;
42 Gun g;
43 w = &b;
44 l->loadFeatures(w);
45 w = &g;
46 l->loadFeatures(w);
47 return 0;
48}
خروجی
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 بسازیم.
1class Weapon
2{
3 public:
4 virtual void features()
5 { cout << "Loading weapon features.\n"; }
6};
مثال: استفاده از تابع مجازی برای حل مشکل
1#include <iostream>
2using namespace std;
3class Weapon
4{
5 public:
6 virtual void features()
7 { cout << "Loading weapon features.\n"; }
8};
9class Bomb : public Weapon
10{
11 public:
12 void features()
13 { this->Weapon::features();
14 cout << "Loading bomb features.\n";
15 }
16};
17class Gun : public Weapon
18{
19 public:
20 void features()
21 {
22 this->Weapon::features();
23 cout << "Loading gun features.\n";
24 }
25};
26class Loader
27{
28 public:
29 void loadFeatures(Weapon *weapon)
30 {
31 weapon->features();
32 }
33};
34int main()
35{
36 Loader *l = new Loader;
37 Weapon *w;
38 Bomb b;
39 Gun g;
40 w = &b;
41 l->loadFeatures(w);
42 w = &g;
43 l->loadFeatures(w);
44 return 0;
45}
خروجی
Loading weapon features. Loading bomb features. Loading weapon features. Loading gun features.
همچنین توجه کنید که تابع l->loadFeatures(w) تابع کلاسهای مختلفی را بسته به این که شیء l به چه چیزی اشاره میکند فرامیخواند.
قبل از مطالعه ادامه مطلب توجه کنید که بیشتر توابع تعریف شده از کلمه کلیدی Void استفاده میکنند. برای درک این کلمه میتوانید مطلب Void در برنامه نویسی چیست؟ همراه با توضیح کاربردها به زبان ساده را مطالعه کنید.
استفاده از تابع مجازی نهتنها موجب شده کد ما روشنتر شود، بلکه انعطافپذیری آن را نیز افزایش داده است. در برنامه فوق، ویژگیهای سلاح دو بار پرینت شدهاند. با افزودن کد بیشتر میتوانید کاری کنید که ویژگیهای سلاح تنها یک بار بارگذاری شود. اگر بخواهیم سلاح دیگری (مانند چاقو) اضافه میکنیم میتوانیم به سادگی ویژگیهای آن را به صورت زیر اضافه کرده و بارگذاری کنیم.
1class Knife : public Weapon
2{
3 public:
4 void features()
5 {
6 this->Weapon::features();
7 cout << "Loading knife features.\n";
8 }
9};
و در تابع ()main به صورت زیر عمل میکنیم:
1Knife k;
2w = &k;
3l->loadFeatures(w);
لازم به ذکر است که ما برای بارگذاری ویژگیهای چاقو، هیچ چیز را در کلاس Loader تغییر ندادهایم.
کلاس مجرد در ++C و تابع مجازی خالص
هدف برنامهنویسی شیءگرا، تقسیم کردن یک مسئله پیچیده به مجموعههای کوچکتر است. بدین ترتیب میتوانیم مسئله را به روشی بهینه درک کرده و حل کنیم.
برخی اوقات استفاده از وراثت صرفاً به منظور بصریسازی بهتر مسئله مفید خواهد بود. در ++C میتوان یک کلاس مجرد ایجاد کرد که امکان وهلهسازی از آن وجود نداشته باشد، یعنی نمیتوان شیئی از روی این کلاس مجرد ساخت. با این حال میتوان از آن کلاس دیگری مشتق کرد و شیئی را از آن کلاس مشتق شده وهلهسازی نمود.
کلاسهای مجرد کلاسهای مبنایی هستند که وهلهسازی نمیشوند. یک کلاس شامل تابع مجازی خالص به نام کلاس مجرد نامیده میشود.
تابع مجازی خالص
یک تابع مجازی که اعلان آن با =0 پایان یابد یک تابع مجازی خالص است. به مثال زیر توجه کنید:
1class Weapon
2{
3 public:
4 virtual void features() = 0;
5};
در این کد تابع مجازی به صورت زیر است:
1virtual void features() = 0
و کلاس Weapon یک کلاس مجرد است.
مثال: کلاس مجرد و تابع مجازی خالص
1#include <iostream>
2using namespace std;
3// Abstract class
4class Shape
5{
6 protected:
7 float l;
8 public:
9 void getData()
10 {
11 cin >> l;
12 }
13
14 // virtual Function
15 virtual float calculateArea() = 0;
16};
17class Square : public Shape
18{
19 public:
20 float calculateArea()
21 { return l*l; }
22};
23class Circle : public Shape
24{
25 public:
26 float calculateArea()
27 { return 3.14*l*l; }
28};
29int main()
30{
31 Square s;
32 Circle c;
33 cout << "Enter length to calculate the area of a square: ";
34 s.getData();
35 cout<<"Area of square: " << s.calculateArea();
36 cout<<"\nEnter radius to calculate the area of a circle: ";
37 c.getData();
38 cout << "Area of circle: " << c.calculateArea();
39 return 0;
40}
خروجی
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 تعریف شده است:
1virtual float area() = 0;
یک نکته که باید اشاره کنیم این است که تابع مجازی خالص کلاس مبنا باید در کلاس مشتق شده باطل (Override) شود. اگر این کار صورت نگیرد، کلاس مشتق شده خود به یک کلاس مجرد تبدیل میشود. برای مطالعه بخش بعدی روی لینک زیر کلیک کنید:
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامه نویسی C++
- مجموعه آموزشهای دروس علوم و مهندسی کامپیوتر
- کپسولهسازی در ++C و C — به زبان ساده
- مبانی ++C برای یادگیری ساختمان داده — به زبان ساده
==
چهارتا ویژگی بارگذاری میکنه چون بدون مجازی با سازنده نوشتین دلیل و فرقش یه چیز دیگس