PDA

View Full Version : سوال: کلاس مشتق و کلاس پایه



قله بلند
پنج شنبه 15 بهمن 1388, 19:44 عصر
با عرض سلام
خیلی وقت ها پیش می آید که کسی فکر می کند مطلبی را بلد است ولی هر چه بیشتر پیش می رود تازه می فهمد که اصل را خوب نفهمیده است. حالِ امروز من هم دقیقاً همینگونه است. تا حالا فکر می کردم که ارث بری را می دانم ولی فهمیدم که نه، آنچه که می دانستم کف بوده نه آب. سیراب که نشدم هیچ حالا تشنه تر شده ام.
و اما سوالی که دارم:فرض کنید کلاس پدر و فرزند را داریم یعنی کلاس پایه و مشتق. این را می دانیم که کلاس مشتق، اوصاف کلاس پایه را به ارث می برد و ممکن است خودش نیز اقلامی را به آن اضافه کند. حالا می خواهیم نمونه ای از کلاس مشتق ایجاد کنیم. این را نیز می دانیم که با تولید نمونه ای از کلاس مشتق، سازنده کلاس پایه برای مقداردهی اولیه به فیلدهای کلاس پایه صدا زده می شود. وقتی سازنده کلاسی صدا زده می شود یعنی اینکه نمونه ای از آن کلاس ساخته شده است؟ یعنی با ساخته شدن نمونه ای از کلاس مشتق، نمونه ای از کلاس پایه نیز تولید می شود؟
اگر اینگونه است، پس ما باید الان دو تا آدرس داشته باشیم یکی برای شیء کلاس مشتق و دیگری برای شیء کلاس پایه.
از طرفی نیز می دانیم که کلاس پایه و کلاس مشتق کاملاً از یکدیگر مستقل هستند و می توانند مستقلاً تولید نمونه کنند و با متغیر ارجاع خود به فیلدها و توابع خود دسترسی پیدا کنند.
در ضمن من این جمله را از کتاب چگونه با C++‎‎ برنامه بنویسیم آقای قلزم خواندم که:یک شیء از نوع کلاس مشتق شده می تواند به صورت یک شیء از کلاس پایه مورد استفاده قرار بگیرد.
خلاصه اینکه هنوز نفهمیدم که با تولید نمونه ای از کلاس مشتق آیا نمونه ای از کلاس پایه نیز تولید می شود یا اینکه رویهم یک شیء تولید می شود؟

قله بلند
جمعه 16 بهمن 1388, 23:44 عصر
سلام. ممنون راجع به پاسختون. شما یک مطلب کاملاً جدید را رو به روی چشمان من قرار دادید. یعنی
public:Base base;تا به حال از کسی نشنیده بودم و جایی ندیده بودم این تعبیر را. با این کار علاوه بر اینکه شگفتی من را برانگیختید، من را گیج تر کردید. فکر کنم فقط خود شما می توانید پرده ابهام را کنار بزنید. لطفاً مطلب را بیشتر باز کنید. راستی راجع به پی دی افی که قرار بود آپلود کنید.

قله بلند
شنبه 17 بهمن 1388, 00:14 صبح
یک مطلبی به ذهنم رسید.وقتی یکی از اعضای داده ای کلاس از جنس کلاس دیگری باشد، با تولید یک نمونه از کلاس مورد نظر که در اینجا کلاس فرزند است، کلاسی که در دل کلاس مورد نظر است که در اینجا منظور کلاس پدر است، نمونه ای از کلاس پدر نیز تولید خواهد شد.یعنی در حافظه، فضایی برای کلاس فرزند در نظر گرفته می شود که این فضا شامل داده ها و توابع کلاس فرزند می شود.با این تعریف، کلاس پدر نیز جزو داده های کلاس فرزند محسوب شده و لاجرم فضایی باید برای آن روی این حافظه اختصاص یابد. با این تعریف اینگونه به نظر می رسد که کلاس پدر و فرزند شده اند یک کلاس و وقتی قرار است نمونه ای از کلاس فرزند ایجاد شود، گویی یک شیء ایجاد شده است، پس آدرس حافظه نیز یکی است نه دو تا. درست است؟

قله بلند
شنبه 17 بهمن 1388, 23:44 عصر
سلام. عجب بحث جالب و در عین حال پیچیده ای است. این تعریفی که شما از این متغیر پنهان می کنید خیلی جالب است ولی بحث جدیدی را به وجود می آورد و انهم ترکیب است. در جایی خواندم که وجود ارث بری تولید یک انقیاد پویا در زمان اجرا را باعث می شود. آیا ترکیب نیز یک انقیاد پویا است؟ چون زمانیکه شیء کلاس مشتق ایجاد می شود، کلاس پایه نیز تولید می شود. چون در زمان اجرا این اتقاف می افتد باعث یک انقیاد پویا می شود. در دو کلاسی که به صورت ترکیب با هم پیوند می خورند نیز باید همینگونه باشد. درست است؟ چون وقتی کلاسی که حاوی اعلان کلاس دیگری است، با تولید آن، کلاس درونی نیز تولید می شود و خود به خود این انقیاد شکل می گیرد. لطفاً این مورد را نیز توضیح دهید. اینقدر این بحث به نظرم پر بحث است که نمی دانم از کجا سوال کنم. به هر حال ممنونم که با صبر و حوصله پاسخ می دهید.

قله بلند
دوشنبه 19 بهمن 1388, 19:06 عصر
استفاده مجدد کد از طریق وراثت:
ارتباط از طریق وراثت باعث سختی در دگرگونی واسط یک کلاس پایه می­شود.
#include iostream.h
#include conio.h
class Fruit {
public:
int peel() {
cout "Peeling is appealing." endl;
return 1;
}
};

class Apple: public Fruit {
};
int main() {
Apple apple;
int pieces = apple.peel();
}زمانیکه شما برنامه کاربردیExample1 را اجرا می­کنید، جمله Peeling is appealing چاپ می­شود، زیرا کلاس سیب از پیاده­سازی تابع peel() ارث می­برد(استفاده مجدد می­کند). اگر در آینده شما بخواهید مقدار برگشتی تابع peel() را به Peel تغییر دهید، شما باید این کد را برای برنامه Example1 نقض کنید. تغییری که شما در واسط کلاس میوه ایجاد می­کنید باعث نقض کد برنامه Example1 می­شود اگر چه این برنامه به صورت مستقیم از کلاس سیب استفاده می­کند و هرگز به صورت مستقیم به کلاس میوه اشاره نمی­کند.
#include iostream.h
#include conio.h
class Peel {
private:
int peelCount;
public:
Peel(int peelCount) {
this- peelCount = peelCount;
}
int getPeelCount() {
return peelCount;
}
};

class Fruit {
public:
Peel peel() {
cout "Peeling is appealing." endl;
return Peel peel(1);
}
};

class Apple: public Fruit {
};

int main() {
Apple apple;
int pieces = apple.peel();//Error
}

قله بلند
دوشنبه 19 بهمن 1388, 19:08 عصر
استفاده مجدد کد از طریق ترکیب
ترکیب، راه دیگری را برای کلاس سیب فراهم می­کند تا از پیاده­سازی تابع peel() از کلاس میوه استفاده کند. به جای توسعه کلاس میوه، کلاس سیب می­تواند مرجعی از نمونه کلاس میوه را در خود نگهداری کند و تابع peel() خود را که به سادگی تابع peel() از کلاس میوه را احضار می­کند تعریف کند. مثال زیر را ببینید:
#include iostream.h
#include conio.h
class Fruit {
public:
int peel() {
cout "Peeling is appealing." endl;
return 1;
}
};

class Apple {
private:
Fruit fruit;
public:
int peel() {
return fruit.peel();
}
};

int main() {
Apple apple;
int pieces = apple.peel();
}در روش ترکیب، زیرکلاس، کلاس front-end و کلاس پایه، کلاس back-end است. با وراثت، یک زیرکلاس به صورت خودکار از پیاده­سازی هر تابع کلاس پایه غیر خصوصی که جایگزین نمی­شود ارث می­برد. با وجود ترکیب، کلاس front-end باید به صورت صریح، تابع متناظر در کلاس back-end از پیاده­سازی تابع خودش را احضار کند. بعضی مواقع این فراخوانی صریح را "forwarding"(ارسال) یا "delegating"(محول کردن) احضار تابع به شیءback-end می­نامند.
روش ترکیب برای استفاده مجدد از کد، محصورسازی قوی­تری را نسبت به وراثت ایجاد می­کند، زیرا یک تغییر در کلاس back-end به نقض کدی که در کلاس front-end از آن استفاده می­کند منجر نمی­شود. به عنوان مثال، دگرگونی در نوع برگشتی تابع peel() از کلاس میوه که قبلاً نشان داده شده است، باعث تغییر در واسط کلاس سیب و نقض برنامه کاربردی نمی­شود.
#include iostream.h
#include conio.h
class Peel {
private:
int peelCount;
public:
Peel(int peelCount) {
this- peelCount = peelCount;
}
int getPeelCount() {
return peelCount;
}
};

class Fruit {
public:
Peel peel() {
cout "Peeling is appealing." endl;
Peel peel(1);
return peel;
}
};

class Apple {
private:
Fruit fruit;
public:
int peel() {
Peel peel = fruit.peel();
return peel.getPeelCount();
}
};

int main() {
Apple apple;
int pieces = apple.peel();
}این مثال نشان می­دهد که اثر ripple (تغییر در حالت اولیه باعث می­شود که به صورت موجی روی حالات بعدی و ادامه فرایند تاثیراتی رخ دهد) حاصل از تغییر در کلاس back-end هنگام رسیدن به کلاس front-end متوقف خواهد شد (یا حداقل می­تواند متوقف شود). با اینکه تابع peel در کلاس سیب باید به منظور سازگاری با تغییرات کلاس میوه دستکاری شود، اما برنامه کاربردی Example2 نیازی به تغییر ندارد.

حالا مسائلی که مطرح می­شود این است که در ارث­بری با تغییر در واسط کلاس پایه، برنامه کاربردی نیز تغییر می­کند ولی در ترکیب، این مشکل حل می­شود. درست است؟ یعنی شکنندگی در برنامه کاربردی بسیار مهم است؟ چرا؟ اگر مطلبی در این مورد به ذهنتان می­رسد خوشحال می­شوم آنها را ببینم.http://www.artima.com/designtechniques/compoinh3.html

قله بلند
سه شنبه 20 بهمن 1388, 19:04 عصر
سلام
من مطالبی که درون این پست قرار دادم را خودم ترجمه کرده­ام. شاید ترجمه­ام خیلی شفاف نبوده.
من منظور شما را نفهمیدم. می­گویند که بحث شیء­گرایی زیاد ربطی به زبان ندارد. اگر مثلاً شیء­گرایی را در c++ خوب بفهمیم، شیئ­گرایی در جاوا و C# و... دیگر کاری ندارد. به همین خاطر متنی که ترجمه کرده­ام مربوط به زبان جاوا است.
به همین خاطر باز هم بخش دیگری را ترجمه کرده­ام و امیدوارم مطالب بیشتری از شیءگرایی را بیاموزیم.

انتخاب بین ترکیب و وراثت

اطمینان حاصل کنید که مدل­های وراثت یک نوع ارتباط "هست... یک" را دارد
فلسفه اصلی این است که وراثت تنها باید زمانی استفاده شود که کلاس فرزند نوعی کلاس پایه باشد یعنی رابطه "هست ...یک" بین کلاس پایه و مشتق برقرار باشد. در مثالی که ذکر شد، سیب احتمالاً یک میوه است بنابراین تمایل به استفاده از وراثت بیشتر می­شود. سوال مهمی که ممکن است از خودتان بپرسید این است که آیا رابطه "هست...یک" در طول حیات برنامه کاربردی استمرار دارد یا خیر. به عنوان مثال، شما ممکن است راجع به این موضوع فکر کنید که یک کارمند زمانی یک شخص است که واقعاً در بخشی از زمان نقش یک شخص را بازی کند. اگر این شخص، از کار بی­کار شود چه؟ چنین ارتباط ناپایداری از رابطه "هست...یک"، معمولاً باید با ترکیب مدلسازی شود.

تنها برای استفاده مجدد از کد، از وراثت استفاده نکنید
اگر خواست تمامی شما واقعاً این است که از کد، استفاده مجدد کنید و رابطه "هست...یک" نیز وجود ندارد، از ترکیب استفاده کنید.

تنها برای داشتن چند ریختی، از وراثت استفاده نکنید
اگر تمامی شما تمایل به داشتن چند ریختی دارید اما واقعاً ارتباطی از نوع "هست...یک" بین کلاس­هایتان برقرار نیست از ترکیب همراه با واسط­ها استفاده کنید.

ترجمه شده از :http://www.artima.com/designtechniques/compoinh5.html
سوال: می دانیم سیب یک میوه است. وقتی این ارث بری را با کلاس پیاده سازی می کنیم و شیئی از کلاس سیب را می سازیم، انگار شیئی از کلاس میوه را ساخته ایم؟
جا افتادن این مطلب خیلی برایم خیلی مهم است. طبق بحثی که شما آن را به زیبایی نشان دادید، در وراثت کلاس پایه انگار فیلدی از کلاس مشتق می شود و از طرفی می دانیم که کلاس مشتق می تواند فبلدها و توابع خودش را نیز اضافه کند پس چگونه می شود ادعا کرد که سیب همان میوه است.
جا افتادن این مطلب، سوال های مربوط به پرتاب استثناء ها را نیز پاسخ می دهد.

قله بلند
سه شنبه 20 بهمن 1388, 22:47 عصر
سلام. اولاً چه وبلاگ جالبی بود. سعی می کنم از مطالبش استفاده کنم. حتماً موردی رو که شما اشاره کردید رو دانلود می کنم و می خونم. راستی چقدر زیبا راجع به فیزیک و ریاضی مثال زدید. از لحن کلامتون بر می یاد که یا باید استاد باشید یا پژوهشگر.