تماس درباره   صفحه اصلی
  زبان ++C > ارث بری  
 
 

ارث بری


وراثت اجازه ايجاد يک کلاس پايه عمومی که اشيا در خصوصيات آن مشترک هستند را می دهد. کلاس های مشتق شده خصويات کلاس پايه را به ارث می برند.

کلاس های پايه و مشتق شده
تعريف کلاس مشتق شده
کنترل دسترسی به اعضای کلاس پايه
وراثت چندگانه
سازنده ها و مخرب ها در وراثت


گاهی ناگذيريم کلاس جديدی ايجاد کنيم که شباهت هائی با کلاسی دارد که قبلا ايجاد شده است و می خواهيم عملکردهائی را به آن اضافه کنيم اين عمل توسط ارث بری (inheritance) امکان پذير است. وراثت يکی از پايه های برنامه نويسی شیء گرائی است.


کلاس های پايه و مشتق شده

چند کلاس ممکن است خصوصيات و رفتارهای مشترکی داشته باشند اما هريک شامل خواص و توابع ديگری هم باشد. وراثت اجازه می دهد يک کلاس عمومی تعريف شود که اشيا درخصوصيات آن مشترک هستند و اين کلاس می تواند توسط ساير کلاس ها ارث برده شود و خواص جديدی به آن اضافه شود بدون اينکه تاثيری روی کلاس عمومی داشته باشد.

توارث شباهت بين دو کلاس را با استفاده از مفاهيم کلاس پايه (base) و کلاس مشتق شده (derived) بيان می کند. کلاسی که از آن ارث بری می شود کلاس پايه يا مبنا و کلاس وارث که خصوصيات کلاس پايه را به ارث می برد را کلاس مشتق شده می نامند. کلاس پايه شامل کليه خواص و رفتارهائی است که بين کلاس های مشتق شده مشترک است.

Base and Derived Classes

مثال. کلاس پايه shape را درنظر بگيريد که دارای خاصيت های اندازه، رنگ و موقعيت است. هر شکل می تواند رسم شود، پاک شود، حرکت کند و رنگ شود. هر کدام از اشکال دارای خواص و رفتارهای اضافه تری هستند. برای يک شکل معين بعضی رفتارها ممکن است متفاوت باشد مثلا محاسبه مساحت.

Base and derived example classes

نکته. يک کلاس متشق شده به نوبه خود می تواند کلاس پايه برای ساير کلاس ها باشد.
نکته. اگر کلاس پايه تغيير کند کلاس مشتق شده نيز تحت تاثير اين تغييرات قرار می گيرد.


تعريف کلاس مشتق شده

فرم کلی تعريف يک کلاس مشتق شده به صورت زير است:

class derived : access base
{
   //members of new class;
}

derived نام کلاس جديد است که از کلاس پايه base مشتق شده است. قسمت access اختياری است ولی می تواند public، private يا protected باشد و برای تعيين مجوز دسترسی اعضای کلاس پايه در کلاس جديد بکار می رود. اگر مجوز دسترسی ذکر نشود به اين معنی است که کليه اعضای عمومی کلاس پايه در کلاس مشتق شده به صورت خصوصی خواهند بود.


مثال. کلاس جديد Derived از کلاس Base مشتق شده است. در برنامه اصلی تابع change از کلاس Derived فراخوانی شده که خود دو تابع set و read از کلاس Base را صدا می زند.

#include <iostream.h>
class Base {
   int i;
protected:
   int read() { return i; }
   void set(int ii) { i = ii; }
public:
   Base() { i=0; }
   int value(int m) { return m*i; }
};
class Derived : public Base {
   int j;
public:
   Derived() { j=0; }
   void change(int x) { set(x); cout << read(); }
};
int main() {
   Derived d;
   d.change(10);
   return 0;
}


کنترل دسترسی به اعضای کلاس پايه

کلاس مشتق شده کليه اعضای کلاس پايه را به ارث می برند اما اجازه دسترسی مستقيم به اعضای خصوصی کلاس پايه را ندارند و تنها از طريق توابع عمومی و سازنده به آنها دسترسی دارند.

نحوه دسترسی به اعضای عمومی کلاس پايه در کلاس مشتق شده توسط يکی مجوزهای دسترسی زير که قبل از نام کلاس پايه ذکر می شود مشخص می شود:

• public
• private
• protected

توارث عمومی

با ذکر کلمه public قبل از نام کلاس پايه اعضای عمومی کلاس پايه به عنوان اعضای عمومی کلاس مشتق شده تلقی می شوند و در اختيار کاربر کلاس مشتق شده قرار می گيرد.


مثال. در مثال قبل تابع value از کلاس Base به اعضای عمومی Derived اضافه می شود بنابراين در برنامه قابل دسترسی است.


توارث خصوصی

با حذف کلمه public يا صريحا با ذکر کلمه private يک کلاس پايه می تواند به صورت خصوصی ارث گرفته شود. در توارث خصوصی کلاس مشتق شده کليه اعضای کلاس پايه را دارا خواهد بود اما به صورت مخفی و اعضای عمومی کلاس پايه اعضای خصوصی کلاس مشتق شده خواهند شد. بنابراين يک شی به عنوان يک نمونه از کلاس نمی تواند به اعضای کلاس پايه دسترسی پيدا کند.

نکته. توارث خصوصی برای پنهان کردن لايه زيرين پياده سازی کلاس پايه مفيد است.
نکته. در توارث خصوصی کليه اعضای عمومی کلاس پايه خصوصی می شوند. اگر می خواهيد عضوی قابل رويت شود کافی است نام آن را (بدون آرگومان و مقدار برگشتی) در بخش public کلاس مشتق شده ذکر کنيد.


مثال. چون وراثت خصوصی است تابع speak از کلاس پايه Pet در برنامه قابل دسترس نيست درحاليکه توابع eat و sleep از کلاس پايه به صورت قابل دسترس درآمده اند.

class Pet {
public:
   char eat() { return 'a'; }
   int speak() { return 2; }
   float sleep() { return 3.0; }
   float sleep(int) { return 4.0; }
};
class Goldfish : Pet {      // Private inheritance
public:
   Pet::eat;      // Name publicizes member
   Pet::sleep;      // Both overloaded members exposed
};
int main() {
   Goldfish bob;
   bob.eat();
   bob.sleep();
   bob.sleep(1);
//! bob.speak();      // Error: private member function
}


توارث محافظت شده

اعضای خصوصی هميشه خصوصی هستند اما گاهی می خواهيد اعضائی را از خارج مخفی کنيد ولی در کلاس مشتق شده قابل رويت باشند. کلمه protected می گويد که اعضای محافظت شده برای هر کسی که از اين کلاس ارث می برد قابل دسترس است و برای بقيه خصوصی است.

مثال. توابع set و read از کلاس Base درمثال قبل در کلاس مشتق شده Derived قابل رويت هستند ولی در برنامه مخفی هستند.

با قرار دادن کلمه protected قبل از نام کلاس مشتق شده اعضای محافظت شده و عمومی کلاس پايه به اعضای محافظت شده کلاس مشتق شده اضافه خواهند شد. بنابراين برای وارثين کلاس مشتق شده در دسترس است و برای بقيه پنهان باقی می ماند

نکته. در کليه حالات اعضای خصوصی کلاس پايه در وراثت شرکت نمی کنند و خصوصی باقی می مانند.
نکته. معمولا توارث عمومی است تا رابط کلاس پايه همچنان رابط کلاس مشتق شده باشد.
نکته. توارث محافظت شده خيلی استفاده نمی شود و فقط برای تکميل زبان برنامه نويسی است.
نکته. مناسب ترين روش اين است که اعضای داده ای کلاس را صورت خصوصی تعريف کنيد تا امکان تغيير پياده سازی زيرين حفظ شود. و به وارثين کلاس مجوز دسترسی کنترل شده ای به توابع عضو محافظت شده بدهيد.


وراثت چندگانه

کلاس مشتق شده می تواند از بيش از يک کلاس پايه ارث بری داشته باشد. در توارث چندگانه (multiple inheritance) چند کلاس به عنوان کلاس پايه اضافه می شوند. نام کلاس های پايه توسط کاما از هم جدا می شود. توارث چند گانه مسائلی را پيش می کشد که در مبحث چندريختی توضيح داده خواهد شد.

class derived : access base1, access base2 ,.. { //...


سازنده ها و مخرب ها در وراثت

کلاس پايه و مشتق شده هر يک می تواند شامل توابع سازنده و مشتق شده باشند. وقتی شیئی ايجاد می شود کامپايلر تضمين می کند که کليه سازنده ها فراخوانی می شوند. در سلسله مراتب وراثت فراخوانی سازنده ها از ريشه شروع می شود. در هر سطح ابتدا سازنده کلاس پايه سپس سازنده کلاس مشتق شده فراخوانی می شود. مخرب ها برعکس ترتيب فراخوانی سازنده ها فراخوانی می شوند.

مثال. در برنامه زير کلاس Derived2 از کلاس Derived1 که خود از Base1 ارث بری دارد ارث می برد.

#include <iostream.h>
class Base1 {
   int x;
public:
   Base1 () {cout << "Base1 constructor\n";}
   ~Base1() {cout << "Base1 destructor\n";}
};
class Derived1 : public Base1 {
   int y;
public:
   Derived1() { cout << "Derived1 constructor\n";}
   ~Derived1() { cout << "Derived1 destructor\n";}
};
class Derived2 : public Derived1 {
   int z;
public:
   Derived2() { cout << "Derived2 constructor\n";}
   ~Derived2() { cout << "Derived2 destructor\n";}
};
int main() {
   Derived2 d2;
   return 0;
}

خروجی برنامه به صورت زير است:

Base1 constructor
Derived1 constructor
Derived2 constructor
Derived2 destructor
Derived1 destructor
Base1 destructor


نکته. ترتيب فراخوانی سازنده ها و مخرب ها در در توارث چندگانه هم صدق می کند. سازنده ها به ترتيبی که در ليست مشخص شده اند از چپ به راست فراخوانی می شوند. و مخرب ها برعکس.


ارسال پارامتر به سازنده کلاس پايه

زمانی که تنها سازنده کلاس مشتق شده دارای آرگومان است می توان به سادگی و به صورت متعارف آرگومان را به سازنده ارسال نمود. اما برای ارسال آرگومان به سازنده کلاس پايه دچار مشکل می شويد چون سازنده کلاس مشتق شده به داده خصوصی کلاس پايه دسترسی ندارد و نمی تواند آنها را مقداردهی کند. برای اين کار C++ گرامری را در اختيار می گذارد که ليست مقداردهی سازنده (constructor initializer list) نام دارد. ليست مقداردهی سازنده امکان فراخوانی صريح سازنده ها از اشيای عضو را می دهد. فرم کلی آن برای سازنده کلاس مشتق شده به صورت زير است:

Derived(arg_list) : Base1(arg_list), Base2(arg_list), ...
{ //body of derived constructor}

نام کلاس های پايه توسط کاما از هم جدا می شوند. Base1 و Base2 و ... نام کلاس های پايه هستند که توسط کلاس مشتق شده Derived به ارث برده می شوند. سازنده ها همگی قبل از اينکه وارد بدنه سازنده کلاس مشتق شده شويد فراخوانی می شوند.


مثال. کلاس Circle از کلاس Point مشتق شده است. در سازنده کلاس Circle سازنده Coint فراخوانی می شود.

#include <iostream.h>
class Point {
   int x,y;
public:
   Point(int atx,int aty ) {x = atx; y = aty;}
   ~Point(){ cout << "Point Destructor called\n";}
   virtual void Draw() { cout << "Draw point at " << x << " " << y << endl;}
};
class Circle : public Point {
   int radius;
public:
   Circle(int atx, int aty, int theRadius) ;
   ~Circle() ;
   virtual void Draw() ;
};
Circle::Circle(int atx,int aty,int theRadius) : Point(atx,aty) {
   radius = theRadius;
}
inline Circle::~Circle() {
   cout << "Circle Destructor called" << endl;
}
void Circle::Draw( void ) {
   Point::Draw();
   cout << "circle::Draw point " << " Radius " << radius << endl;
}
int main() {
   Circle ACircle(10,10,5) ;
   ACircle.Draw();
   return 0;
}


برخلاف اينکه اغلب نياز است سازنده ها به صورت مستقيم در ليست مقداردهی فراخوانی شوند، مخرب ها نيازی به فراخوانی صريح ندارند زيرا هر کلاس تنها يک مخرب بدون هيچ آرگومانی دارد. کامپايلر کليه مخرب ها را از آخرين کلاس مشتق شده به سمت ريشه در کل سلسله مراتب وارثت اجرا می کند.


 


 


صفحه اصلی| PDF| درباره| تماس