יום שלישי

Constructors

ה-constructors היא פונקציה לה שני תפקידים:
1. לצור אובייקט חדש.
2. לאתחל את ה-data members של האובייקט בערכים.
ה-constructor מופעל פעם אחת עבור כל אובייקט בזמן יצירתו.

מה קורה אם לא מספקים constructor עבור class כלשהו?
במקרה זה הקומפיילר יספק default constructor שיצור את האובייקט.

 איך כותבים constructor?
 בדומה לפרוצדורה רגילה ב-class, אך יש לשים לב לשני המאפיינים העיקריים:
1. שם ה-constructor צריך להיות זהה לשם ה-class.
2. למתודה אין שום type גם לא void. היא בכל מקרה לא מחזירה שום ערך.

נראה דוגמא.
נחזור שוב ל- Rect class שהופיע בפוסט הקודם, שדן במבוא ל-class.
הדגמנו שם יצירה של אובייקט מסוג Rect וזה נראה כך:
Rect rect;
הזכרנו שם את העובדה שמופעל שם ה-default constructor. 

כעת, נספק constructor שאינו ה-default, ונפעיל אותו.
הנה שתי צורות לקריאה לconstructor:
צורה  א - explicit call:
Rect rect = Rect(a,b);
כאן הגדרנו במפורש את הקריאה ל-constructor כלומר explicit call. 
צורה ב - implicit call:
Rect rect(a,b);
כאן הקריאה ל-constructor לא נעשית במפורש, אלא משתמעת = implicit call.
התוצאה זהה בשתי צורות הסימון.


אז הנה שוב התוכנית עם ה-class שמגדיר מלבן. להזכירכם, ה-class members של Rect כוללים את:
- a ו-b שהן צלעות המלבן,
- המתודות set ו-show.

הנה הגדרת ה-class. הפעם  עם תוספת constructor,  שלמעשה מוצגים שלושה  constructors ע"י overloading - שורות 16-18. (הסבר נוסף בהמשך).

  1. /*
  2.  * rect.h
  3.  *
  4.  *  Created on: Mar 12, 2012
  5.  *      Author: ronen
  6.  */


  7. #ifndef RECT_H_
  8. #define RECT_H_
  9. class Rect{
  10. private:
  11. int a;
  12. int b;
  13. public:
  14. Rect(void);
  15. Rect(int a);
  16. Rect(int a, int b);
  17. void set(int a, int b);
  18. void show();
  19. };

  20. #endif /* RECT_H_ */
שורה 16: מוגדר כאן Constructor. כפי שציינו, אין לו type, והוא לא מחזיר ערך אף פעם, ושם ה-constructor זהה לשם ה-class. 
שורה 17: שוב הגדרה של constructor, הפעם עם prototype שונה - יש לו פרמטר יחיד מסוג  int. איך יתכן שמתודה עם שם זהה מוגדרת יותר מפעם אחת? הדבר נעשה בזכות ה-polymorphism. תכונה זו מאפשרת הגדרה של אותה מתודה במספר צורות שונות. המערכת תדע להפעיל את המתודה המתאימה לפי הפורמט שבא היא נקראת. נדגים זאת מיד.
שורה 18: הגדרה נוספת של constructor. הפעם הוא מקבל שני פרמטרים. ע"ע polymorphism.
שורות 19 ו-20 הן מתודות רגילות החברות ב-class. ראינו אותן בפוסט הראשון בנושא class.

הנה המימוש של מתודות ה-class:
  1. //============================================================================
  2. // Name        : rect.cpp
  3. // Author      : ronen halevy
  4. // Version     :
  5. // Copyright   : Your copyright notice
  6. // Description : 
  7. //============================================================================
  8. #include "rect.h"
  9. #include <iostream>
  10. using namespace std;

  11. Rect::Rect(void){
  12. a = 10;
  13. b = 20;
  14. }
  15. Rect::Rect(int edge_a){
  16. a=edge_a;
  17. b = 30;

  18. }
  19. Rect::Rect(int edge_a, int edge_b){
  20. a=edge_a;
  21. b=edge_b;
  22. }

  23. void Rect::set(int edge_a, int edge_b){
  24. a = edge_a;
  25. b = edge_b;
  26. cout << "from set method: a= " << a<<"  b= " << b << endl;

  27. }
  28. void  Rect::show(){
  29. cout << "edge a = " << a << "\nedge b = " << b  << "\narea = " << a*b << endl;
  30. }

בקובץ הנ"ל הדגשתי בצבע את המימוש של 3 סוגי ה-constructors. הראשון לא מקבל פרמטרים בכלל (שורה 12) , השני מקבל פרמטר אחד (שורה 16) והשלישי שניים (שורה 21).
בכל מקרה, ה-constructors מאתחלים את ערכי a ו-b. (באופן עקרוני, אפשר כמובן לבצע ב-constructor פעילות לפי בחירתינו.)


נעבור למתודת ה-main:

  1. /*
  2.  * my_rect.cpp
  3.  *
  4.  *  Created on: Mar 12, 2012
  5.  *      Author: ronen
  6.  */
  7. #include <iostream>
  8. #include "rect.h"
  9. using namespace std;
  10. int main(void){

  11. cout << "Constructor 1:" << endl;
  12. Rect rect1 = Rect();
  13. rect1.show();

  14. cout << "Constructor 2:"<< endl;
  15. Rect rect2 = Rect(400);
  16. rect2.show();

  17. cout << "Constructor 3:" << endl;
  18. Rect rect3(100,200);
  19. rect3.show();

  20. cout << "Constructor 4:" << endl;
  21. Rect rect4;
  22. rect4.show();

  23. return 0;
  24. }




ה-main מדגימה הפעלה של 4 constructors. שלושה מהם הגדרנו ב-class, ועוד אחד בשור 26. הנה ההסבר.
שורות 12-14:
שורה 12  מדפיסה כותרת להקלת ההתמצאות בפלט.
שורה 13 יוצרת את האובייקט rect1 מסוג Rect. החידוש כאן - הפעלה של ה-constructor. על פי ה-prototype, תדע המערכת להפעיל את ה-constructor הנכון (ע"ע polymorohism או overloading). במקרה הנ"ל, אין למתודה פרמטרים, כך שהיא תפעיל את המתודה שהוגדרה בשורה 12 של הקובץ הקודם. העתקתי אותה הנה שוב:

  1. Rect::Rect(void){
  2. a = 10;
  3. b = 20;
  4. }
שורה 14 מפעילה את המתודה show, להצגת הערכים/
באופן דומה בקטעים שמתחילים בשורות 16 ו-20 מיוצרים אובייקטים נוספים מסוג Rect, וכל אחד מהם מפעיל constructor מסוג אחר.
בשורה 22 הקריאה ל-constructor נעשית implicit, שהיא פשוט הצגה קומפקטית יותר (ראה הסבר למעלה).

אבל מה קורה אם לא מפעילים constructor?
שימו לב לשורה 26: 
Rect rect4;
יוצרים אובייקט, אבל אין כביכול קריאה ל-constructor. כאן תתכנה שתי אפשרויות:
1. אם לא היה מוגדר שום constructor, המערכת היתה מפעילה default constructor שלמעשה נראה כך:
Rect:Rect(){}
אבל, אם מוגדר constructor כלשהו, גם שורה 26 תדרוש קיום constructor שיתאים לה, כלומר ללא פרמטרים. במקרה שלנו, ה-constructor משורה 12 בקובץ rect.cpp יופעל ע"י שורה 26 הנ"ל.


הנה פלט ההרצה. ההדפסה נוצרה ע"י המתודה show שמןפעלת אחרי כל אחד מ-4 ה-constructors.

Constructor 1:
edge a = 10
edge b = 20
area = 200
Constructor 2:
edge a = 400
edge b = 30
area = 12000
Constructor 3:
edge a = 100
edge b = 200
area = 20000
Constructor 4:
edge a = 10
edge b = 20
area = 200

עד כאן ההסבר על constructors. יהיו בודאי פוסטים נוספים על constructors. 

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

תגובה 1:

  1. תודה רבה על ההסבר :) הייתי שמח כמתכנת מתחיל במדריכים קצת לקשר אותי למה הצורך בזה, (מוטיבציית שימוש) . ::)

    השבמחק