יום חמישי

Virtual Methods

הפוסט הזה עוסק בהבטים של -inheritance ו-polymorphism. הוא מציע איך להתמודד עם מצב בו ה-derived class יורש את ה-base class, והוא מממש מתודות הקיימות ב-base class.
 נרצה למנוע מצב בו המערכת תפנה להפעיל את המתודה ב-class המקורי, בעוד אנו רוצים להפעיל את המתודה החדשה. מצב כזה אכן יתכן, והשימוש ב-virtual method מונע אותו ופותר את הבעיה.
כרגיל, נלמד בעזרת דוגמא פשוטה. נציג שני classes, האחד base והשני derived. נדגים קביעה של אחת המתודות כ-virtual. ובפרוט רב יותר:
נציג class שנותן הערכה למחיר תפוזים.
הנוסחאות בהן משתמש ה-class:
price = weight * price_per_kg
כאשר
weight = num_of_items/oranges_in_kg
ההנחה היא כי: 4=oranges_in_kg, כלומר 1קג = 4 תפוזים.
לאחר מכן ניצור class דומה עבור תפוחים תוך שימוש ב-inheritance ו-virtual methods.

הנה ה-class שמחשב מחיר התפוזים. הוא יהיה ה-base class שבקרוב נצור ממנו את ה-Apples class:
  1. /*
  2.  * oranges.h
  3.  *
  4.  *  Created on: Mar 15, 2012
  5.  *      Author: ronen halevy
  6.  */

  7. #ifndef ORANGES_H
  8. #define ORANGES_H
  9. class Oranges{
  10. protected :
  11. static double const oranges_in_kg = 4;
  12. double num_of_items;
  13. double price_per_kg;
  14. public:
  15. Oranges(double inum_of_oranges, double iprice_per_kg){num_of_items=inum_of_oranges;price_per_kg=iprice_per_kg;}
  16. virtual double estimate_weight(){return (num_of_items/oranges_in_kg);}
  17. double estimated_price(){return(estimate_weight()* price_per_kg);}
  18. };

  19. #endif /* ORANGES_H */



נעבור על מספר נקודות ב-class  הנ"ל:
ה-class מכיל constructor (שורה 16) ושתי מתודות (שורות 17-18). כולם ממומשים inline.
במימוש inline המתודה ממומשת יחד עם ההגדרה שלה. מתאים למתודות קטנות.
שורה 18 מחשבת את המחיר. הנוסחה לחישוב המחיר היא:
estimate_weight()* price_per_kg
המתודה estimate_weight מוגדרת כ-virtual. למה virtual? כי זו המתודה שנממש שוב ב-derived class.

והנה דוגמא ל-main שיוצר אובייקט מסוג Oranges:


  1. int main(){
  2. Oranges orange(4,5);
  3. cout << " calculated price oranges= "<<orange.estimated_price()<<"\n";
  4. }

שורה 2: יצירת האובייקט, כאשר הפרמטאים עבור ה-constructor הם 4 ו-5 המתייחסים לכמות התפוזים ומחיר לק"ג בהתאמה.
הפלט שהתקבל:
 calculated price oranges= 5
כלומר המחיר שחושב עבור  4 תפוזים, כשהמחיר לק"ג הוא 5, ומספר התפוזים בק"ג הוא 4
(static double const oranges_in_kg = 4;)

הוא: 5 ש"ח.


כעת ניצור class המחשב מחיר של תפוחים.
ה-class זהה לזה של Oranges מלבד המתודה לחישוב ה-weight. במקרה של תפוזים החישוב נעשה לפי 4 תפוזים לק"ג. עבור התפוחים החישוב יהיה לפי 8 תפוחים לק"ג. נכתוב לשם כך מחדש את המתודה לחישוב ה-weight. 

הנה ה-Apples class:


  1. #include "oranges.h"

  2. #include <iostream>
  3. #include <string>
  4. using namespace std;


  5. class Apples: public Oranges{
  6. static double const apples_in_kg = 8;
  7. public:
  8. Apples(double inum_of_items, double iprice_per_kg):Oranges(inum_of_items, iprice_per_kg){};
  9. double estimate_weight(){return(num_of_items/apples_in_kg);}
  10. };



שורה 5: ה-class יורש את Oranges.
שורה 9: הגדרת הקבוע שמשמש לחישוב המשקל, (או יותר נכון הערכת המשקל): כל 8 תפוחים שוקלים ק"ג.
שורה 11: ה-constructor, שדואג להעביר את הפרמטרים גם ל- Oranges constructor.
שורה 12: כאן  estimate_weight נכתבה מחדש. היות שהיא הוגדרה ב-base class כ-virtual, מובטח לנו שהיא תרוץ מכן - מה-derived class, ולא מה-base.


והנה הmain:

  1. int main(){
  2. Oranges orange(4,5);
  3. cout << " calculated price oranges= "<<orange.estimated_price()<<"\n";
  4. Apples apple(4,5);
  5.   cout << " calculated price apples= "<<apple.estimated_price()<<"\n";
  6. }

בשורה 2 יוצרים אובייקט מסוג Orange כשערכי ההתחלה הם 4 תפוזים במחיר של 5 שח ל ק"ג.
בשורה 3 מחשבים את המחיר ומציגים אותו.
בשורה 4 ו-5 חוזרים על אותו תהליך עם אובייקט מסוג Apples. שוב: 4 תפוחים במחיר של 5 ש"ח לק"ג.

איך יראה הפלט?
הנה:

 calculated price oranges= 5
 calculated price apples= 2.5

יש שם 8 תפוזים, והנחנו שבק"ג יש 4 תפוזים. מכאן, משקל התפוזים 1 ק"ג והמחיר 5 שח.
יש שם 4 תפוחים, והנחנו שבק"ג יש 8 תפוחים. מכאן, משקל התפוחים0.5 ק"ג והמחיר 2.5 שח.

מה היה הפלט אם  estimate_weight לא היה מוגדר כ virtual function ? במקרה זה, היה נעשה שימוש ב:
Oranges:estimate_weight גם עבור התפוחים והתוצאה היתה:

 calculated price oranges= 5
 calculated price apples= 5

הערה: למען הסר ספק, בחלק גדול מהמקרים בהם מתודה נכתבה מחדש ב-dervied class, המתודה החדשה תקרא גם ללא שימוש ב-virtual fuction.
דוגמא: - להוסיף דוגמא----



קישור להורדת קבצי המקור.



3 תגובות:

  1. זה היה מועיל מאוד, תודה רבה!

    השבמחק
  2. תודה רבה על הפרסום, עזר לי בהבנת Virtual Function.

    השבמחק