PDA

View Full Version : سوال: وراثت چندگانه !؟



A.S.Roma
چهارشنبه 22 خرداد 1387, 09:03 صبح
با سلام

فرض کنید یک کلاس داریم به نام Circle که مثلا خصوصیات یک دایره را شامل می شود ( مثل x,y,r ) .
کلاس دیگری به نام FilledCircle از کلاس بالا مشتق شده و یک دایره تو پر را تولید می کند .
همچنین کلاس دیگری به نام MovedCircle از کلاس اولی ( Circle ) مشتق شده و دایره را حرکت می دهد .
حال اگر یک کلاس به نام MovedFilledCircle از کلاس های دوم و سوم به ارث ببریم ، که وظیفه اش حرکت یک دایره تو پر باشد عملا شاهد این نتیجه نخواهیم بود .
البته مشکل کار مشخص است که کلاس های Filled و Moved هر کدام یک x,y دارند و کلاس پایین دست ( MovedFilled ) در حین انجام Move یکی از این x,y ها را دستکاری می کند و به همین جهت فقط پوسته دایره حرکت می کند و داخل دایره ثابت می ماند .

چه طور می توان این مشکل را حل کرد !؟

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

ممنون !

PC2st
چهارشنبه 22 خرداد 1387, 17:18 عصر
میشه از virtual function ها و virtual inherit کمک گرفت، اگه حوصله خوندن توضیحات زیر رو ندارید (که البته توضیحات هم کامل نیست و درباره قسمت virtual inherit اش زیاد توضیح ندادم)، بهتره که مستقیما سورس کد رو دانلود کنین! :لبخند:
مثلا کلاس Circle اینطوریه:


class Circle
{
public :
Circle(int x, int y, int r);
virtual void Show();
protected :
int x, y, r;
};

و کلاس FilledCircle این شکلی:


class FilledCircle : public Circle
{
public :
FilledCircle(int x, int y, int r, int fill);
virtual void Show();
private :
int fill;
};

در اینصورت پیاده سازی دلخواهتون رو برای تابع Show انجام بدین بطوریکه مثلا دایره رو توپر رسم کنه، بطوریکه اول تابع Show از کلاس پدر
(Circle) رو صدا میزنن تا اطراف دایره رسم بشه و بعدش خودتون محتویات درون دایره رو سم کنید. مثلا:


void FilledCircle::Show()
{
Circle::Show(); //draw circle borders... called from base class.
... //then fill inside the circle with colors or pattern.
}

همانطور که در بالا دیدید، دیگه لازم نیست دایره رو کلاس FilledCircle رسم کنه و میتونه از تابع کلاس والدش برای ترسیم حاشیه های دایره
استفاده کنه و بحاش خودش یک رنگ (یا الگو) رو برای پر کردن دایره در داخل دایره رسم میکنه.
حالا فرضا کلاس MovedCircle اینطوره:


class MovedCircle : public Circle
{
public :
MovedCircle(int x, int y, int r);
void Move(int step);
virtual void Show();
private :
int current_position;
};

که مثلا تابع Move کار حرکت دادن دایره رو به عهده داره:


void MovedCircle::Move(int step)
{
current_position += step;
... //do any thing that you need.
}

برای کلاس فوق هم باید پیاده سازی خاص تابع Show رو انجام بدین، یعنی بطور مثال:


void MovedCircle::Show()
{
... //draw moved circle with new its position.
}

که همانطور که در بالا میبینید، دیگه از تابع Show کلاس پدر (Circle) استفاده نکردیم، چون دایره ما دیگه مکانش جابجا شده و نمیشه از ترسیم کلاس
پدر (Circle) استفاده کرد و باید پیاده سازی مخصوص کلاس MovedCircle رو خودمون از اول انجام بدیم.
حالا اگه بخوایم از کلاسهای قبلی استفاده کنیم، به این شکل استفاده میشه:


int main()
{
Circle c1 = Circle(4, 4, 8);
cl.Show(); //draw a circle border.
FilledCircle c2 = FilledCircle(3, 5, 12, 2);
c2.Show(); //draw a circle with border and filled inside it.
MovedCircle c3 = MovedCircle(4, 3, 12);
c3.Move(5); //move the circle by 5 step.
c3.Show(); //draw the moved circle border.
}

حالا که کلاسهای فوق رو داریم و مثلا میخوایم کلاس MovedFilledCircle رو ایجاد کنیم، در اینصورت پروتوتایپ این کلاس شبیه به زیر میشه:


class MovedFilledCircle : public MovedCircle, public FilledCircle
{
public :
MovedFilledCircle(int x, int y, int r, int fill);
virtual void Show();
};

در اینصورت، برای کلاس فوق هم کافیست که پیاده سازی دلخواه خودتون رو انجام بدین و مثلا طریقه استفاده از اون هم مثل زیر میشه:


MovedFilledCircle c4 = MovedFilledCircle(3, 4, 15, 2);
c4.Move(5); //move filled circle but don't draw it yet!
c4.Show(); //draw moved filled circle in the special position.

ولی یک ایراد کوچیک بازهم وجود داره که برای رفعش باید از ارث بری بصورت virtual استفاده کرد، یعنی کلاسهای MovedCircle و FilledCircle رو
بصورت virtual از کلاس Circle به ارث رسوند.
یعنی:


class FilledCircle : virtual public Circle
{...};

// و همچنین ...

class MovedCircle : virtual public Circle
{...};


من هم دقیقا عملکردش رو نمیدونم ولی میدونم که باعث میشه کامپایلر متوجه بشه که کلاسهای
MovedCircle و FilledCircle که پدر کلاس MovedFilledCircle هستند، کامپایلر متوجه میشه که اون کلاسها از یک والد یعنی کلاس Circle به ارث
رسیدند و در حال حاضر هم بطور مشترک بعنوان پدران یک کلاس دیگر تعریف شده اند. در هر صورت، بهتره سورس زیر رو بگیرید و کامپایل کنید تا متوجه
شوید.