יום רביעי

מבוא ל-Inheritance

Inheritance (הורשה) מאפשרת פיתוח של class חדש בהתבסס על class קיים. למה צריך את זה? code reuse! נראה את התהליך בעזרת דוגמא.
נגדיר תחילה שני מושגים: 
base class: זה ה-class הקיים, ממנו נרצה לרשת (inherit).
derived class: זה ה-class היורש.

ה-derived class יורש את כל מרכיבי ה-base class, ומוסיף להם חברי class חדשים. למעשה הוא יורש את כל מרכיבי ה-base class, חוץ מ-3 ההסתיגויות הבאות:
1.  ה-constructor וה-destructor לא עוברים בירושה.
2. friends - (ראה פוסט בנושא) - לא עוברים בירושה.
3. לגבי operator overloading - כל האופרטורים חוץ מ = מועברים בירושה.


והנה תאור הדוגמא:
נניח שיש לנו class בשם Citizen. ה-class הזה מכיל את בסיסי הנתונים הקשורים לאזרח רגיל:
ת"ז,
כתובת מגורים,
תאריך לידה,
שם האב,
שם האם.
ה-class מכיל גם מתודות לעדכון כתובת ולהצגה של הנתונים.
כעת ברצוננו לצור class בשם Policeman. ה-class הזה יכיל את הנתונים הבסיסיים של כל אזרח, ובנוסף יכיל:
דרגה, 
מתודה לעידכון הדרגה.

אז ניגש לעבודה ונתחיל בהצגת ה-base class. 
הנה הגדרת ה-class:

  1. /*
  2.  * citizen.h
  3.  *
  4.  *  Created on: Mar 14, 2012
  5.  *      Author: ronen halevy
  6.  */

  7. #ifndef CITIZEN_H_
  8. #define CITIZEN_H_

  9. #include <string>
  10. using namespace std;
  11. class Citizen{
  12. protected:
  13. std::string address;
  14. string birth_date;
  15. int id;
  16. string father_name;
  17. string mother_name;
  18. public:
  19. Citizen(string address, string birth_date, int id, string father_name, string Mother_name);
  20. void show();
  21. void change_address(string new_address);
  22. };
  23. #endif /* CITIZEN_H_ */

נעבור על הנקודות המעניינות בקובץ הנ"ל:
 שורה 11: header file עבור string.
שורה 12: using namespace - עפ"י פקודה זו התוכנה תדע לגשת לספריית std ולמצוא את האובייקט. בלי שורה זו, הסוג string לא ימצא, אלא אם יכתב כ: std:string - כמו בשורה 15.


שורה 14-19: הגדרת חברי ה-class שהם עם access level ברמת protected. מה המשמעות של protected? נסביר: עד עתה נתקלנו ב-access level משני סוגים: public ו-private.
לחבר ב-class מסוג public, ניתן לגשת גם מתוך ה-אובייקט וגם מבחוץ. לחבר class מסוג private ניתן לגשת רק מתוך האובייקט. עד כאן, protected מתנהג כמו private. ההבדל בין protected ל-private קשור בדיוק לעניין ה-inheritance. אל חבר class מסוג private לא ניתן לגשת ישירות מתוך ה-derived class. אל חבר class מסוג protected, ניתן לגשת מתוך ה-derived class. 
בהמשך נראה גישה לחברי ה-class הנ"ל מתוך ה-derived class.

שורה 20: זה ה- constructor/
שורות 21-22: מתודות של ה-class.

נציג את מימוש המתודות של ה-class:

  1. /*
  2.  * citizen.cpp
  3.  *
  4.  *  Created on: Mar 14, 2012
  5.  *      Author: ronen halevy
  6.  */

  7. #include "citizen.h"

  8. #include <iostream>
  9. #include <string>
  10. using namespace std;

  11.  Citizen::Citizen(string address, string birth_date, int id, string father_name, string mother_name){
  12. Citizen::address = address;
  13. Citizen::birth_date = birth_date;
  14. Citizen::id = id;
  15. Citizen::father_name = father_name;
  16. Citizen::mother_name = mother_name;
  17. cout << "\nCitizen constructor. address: " << address << "birth_date: "<<birth_date<<"id: "<<id<<"father's name: "<< father_name<<"mother's name: "<<mother_name;

  18. }

  19. void Citizen::show(){
  20. cout << "\naddress = " << address << "\nbirth_date: "<<birth_date<<"\nid: "<<id<<"\nfather's name: "<< father_name<<"\nmother's name: "<<mother_name;

  21. }

  22. void Citizen::change_address(string new_address){
  23. Citizen::address = new_address;
  24. }


התוכנית הנ"ל כוללת שלוש מתודות - מודגשות בצבעם שונים:
המתודה הראשונה (שורה 14): זהו ה- constructor. הוא מקבל את כל הפרמטר לאתחול רשומת הנתונים של Citizen.
המתודה השניה (שורה 24): show מדפיס לפלט את רשימת הנתונים.
המתושה השלישית (שורה 29): מתודה לעדכון כתובת.


עד כאן תאור ה-base class. מיד נצור ממנו derived class.

הנה ה-policeman: נציג את הגדרת ה-class ואח"כ את מימוש המתודות.

הנה הגדרת ה-class:
  1. /*
  2.  * policeman.h
  3.  *
  4.  *  Created on: Mar 14, 2012
  5.  *      Author: ronen halevy
  6.  */

  7. #ifndef POLICEMAN_H_
  8. #define POLICEMAN_H_

  9. #include "citizen.h"

  10. #include <string>
  11. using namespace std;
  12. class Policeman : public Citizen{
  13. private:
  14. string rank;
  15. public:
  16. Policeman(string rank, string address, string birth_date, int id,  string father_name,  string mother_name);

  17. };
  18. #endif /* POLICEMAN_H_ */

כאן נתייחס לשלושת החלקים המודגשים בצבע:
חלק ראשון - שורה 15: זה המקום בו מוגדירים את ה-base class. ההכרזה:
 : public Citizen
קובעת שני דברים:
1. Citizen הוא ה-base class של Policeman.
2. public היא דרגת ה-access level המקסימלית ב-base class.

נקודה 2 הנ"ל דורשת הסבר. קיימות שלוש דרגות אפשרויות של access. הנה הן  בסדר עולה:
א. private.
ב. protected.
ג. public.

בהגדרת ה-base class, מגדירים את דרגת ה-access המקסימלית. נבחן את התוצאה עבור כל אחת מ-3 הדרגות.
1. נניח שההגדרה ב-class הנ"ל היתה: 
 : private Citizen
במקרה הזה כל חברי ה-base class היו הופכים להיות private.
2. נניח שההגדרה של ה-base class היתה:
 : protected Citizen
במקרה זה, חברי ה-base class עם access level ברמת public היו הופכים ל-protected.
3. ובמקרה שלנו, בו ההגדרה היא (ראה שורה 15):
 : public Citizen

ה-access level של חברי ה-base class נשאר ללא שינוי.

חלק שני - שורה 16-17: הגדרה של חברי ה-derived class מסוג protected. כאן הוספנו משתנה דרגה (rank).
החלק השלישי- שורות 18-21: כולל הגדרת ה-constructor של ה-derived class ועוד מתודה שמאפשרת שינוי הדרגה.

נעבור כעת למימוש ה-constructor והמתודה של ה-class.


  1. /*
  2.  * policeman.cpp
  3.  *
  4.  *  Created on: Mar 14, 2012
  5.  *      Author: ronen halevy
  6.  */
  7. #include "policeman.h""
  8. #include <iostream>
  9. #include <string>
  10. using namespace std;

  11. Policeman::Policeman(string rank, string address, string birth_date,  int id,  string father_name,  string mother_name) : Citizen(address,  birth_date,  id,  father_name,  mother_name){
  12. //Policeman::Policeman(string rank){

  13. Policeman::rank = rank;
  14. cout << "\n Policeman constructor rank is: " << rank;
  15. }

  16. void change_rank(string rank){
  17. Policeman:rank = rank;
  18. }
החלק המעניין כאן הוא ה-constructor - מודגש בצהוב.
נשאת השאלה: מי מפעיל את ה-constructor של ה-base class, ואיך זה מתבצע?
התשובה: ה-constructor של ה-base class מופעל לפני הפעלת ה-constructor של ה-derived class.
* אם לא מוגדר constructor ב-base class, יופעל ה-default constructor.
* אם מוגדר constructor שלא מקבל פרמטרים - הוא יופעל.
* אם מוגדר constructor שמקבל פרמטרים, אפשר להפעיל אותו, כפי שמודגם בשורה 13 כאן למעלה.
הפרמטרים שמועברים ל-base class constructor צריכים להיות מועברים גם ל derived class constructor.
במקרה שלנו לדוגמא, הconstructor נראה כך:
Citizen(string address, string birth_date, int id, string father_name, string mother_name);

הוא מקבל 5 פרמטרים. לכן הגדרתי את ה-constructor של ה-derived class כך:

:Policeman(string rank, string address, string birth_date,  int id,  string father_name,  string mother_name)

הערה - העברת הפרמטרים ל-constructor של ה-base class נעשית על פי הפורמט של ה-initialization list. ראה פוסט בנושא.
זהו ההסבר על ה-constructors.


נציג את main, ונדגים את ההפעלה של מערכת ה-classes הנ"ל.
  1. /*
  2.  * my_inheritance.cpp
  3.  *
  4.  *  Created on: Mar 14, 2012
  5.  *      Author: ronen halevy
  6.  */

  7. #include "citizen.h"
  8. #include "policeman.h""

  9. #include <iostream>
  10. using namespace std;
  11. int main(){
  12.    Policeman policeman("sergeant", "levanon st. tel aviv", "22.2.`1922",  77777777,  "yosef",  "rivka");
  13.    policeman.change_address("11, habonim st. Jerusalem");
  14.    policeman.show();
  15. }
שורה 14: יצירת אובייקט מסוג Policeman והפעלת ה-constructor תוך הכנסת 6 הפרמטרים. כמו שהוסבר קודם, רק אחד מהפרמטרים ("sergant") באמת דרוש לderived class constructor. שאר 5 הפרמטרים דרושים עבור ה-base class constructor.
שורה 15: כאן ניתן לראות שהמתודה change_address השייכת ל-base class, הועברה בירושה ל-derived class.
16: גם המתודה show שייכת במקור ל-Citizen, אבל העברה בירושה ל-Policeman.

הנה הפלט שהודפס על הצג:


  1. Citizen constructor. address: levanon st. tel avivbirth_date: 22.2.`1922id: 77777777father's name: yosefmother's name: rivka
  2.  Policeman constructor rank is: sergeant
  3. address = 11, habonim st. Jerusalem
  4. birth_date: 22.2.`1922
  5. id: 77777777
  6. father's name: yosef
  7. mother's name: rivka
שורה 1 הודפסה ע"י ה-base class constructor.
שורה 2 הודפסה ע"י ה-derived class constructor
שורות 3-7 הודפסן על ידי המתודה show.

זהו המבוא ל-inheritance.

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


אין תגובות:

הוסף רשומת תגובה