Download presentation
Presentation is loading. Please wait.
Published byDeborah Kelly Modified over 6 years ago
1
תכנות מכוון עצמים ו- C++ יחידה 03 מחלקות: תכונות, שיטות, הרשאות, const
קרן כליף
2
ביחידה זו נלמד: מהי מחלקה פניה אל תכונות ושיטות המחלקה הרשאות גישה
מחלקה המכילה מחלקה אחרת העברת פרמטר by ref בעיית ה- include הכפולים פרמטר const שיטת const ערך מוחזר const משתנה const constexpr מתודות inline
3
תכנות מונחה עצמים (OOP)
הבסיס התפיסתי של תכנות מכוון עצמים הוא שכך מאורגן העולם: בעצמים כל דבר הוא אובייקט: כסא, שולחן, סטודנט, מרצה וכד' לכל אובייקט יש מאפיינים המייצגים אותו למשל, לסטודנט יש שם, ת.ז. וממוצע לכל אובייקט יש פעולות שהוא יודע לעשות למשל, סטודנט יכול לעשות שיעורי בית, ללמוד למבחנים וללכת לים
4
העולם מורכב מאובייקטים
סרט הוליוודי מצולם או באולפנים או במיקום כלשהו בעולם. לכל סרט יש במאי, תסריט, מפיק ורשימת שחקנים שמשתתפים בסרט. חלק מהשחקנים הם שחקנים ראשיים וחלק שחקני משנה, כמו כן יש ניצבים. לכל שחקן ראשי יש לפחות אוסקר אחד בעברו אובייקטים: סרט במאי תסריט מפיק שחקן: ראשי, משנה, ניצב
5
לאובייקטים יש מאפיינים
נתכנן תוכנה לניהול נתוני הסרט אחד מהאובייקטים יהיה השחקנית ג'וליה רוברטס: שם: ג'וליה רוברטס תאריך לידה: 28/10/1967 סרט מפורסם: "אישה יפה" מספר פרסים: 50
6
אובייקטים יכולים לעשות דברים - שיטות
שיטות ש"ג'וליה רוברטס" יודעת לעשות: לככב במדורי רכילות להיות מועמדת לפרס להצטלם על השטיח האדום
7
האובייקט ג'וליה רוברטס - סיכום
תכונות של האובייקט ג'וליה רוברטס: להיות מועמדת לאוסקר להצטלם על השטיח האדום לככב במדורי רכילות שם: ג'וליה רוברטס תאריך לידה: 28/10/1967 סרט מפורסם: "אישה יפה" מספר פרסים: 50 פעולות ג'וליה רוברטס יכולה לבצע:
8
אובייקטים נוספים דומים
שם: רוברט רדפורד תאריך לידה: 18/08/1936 סרט מפורסם: "הצעה מגונה" מספר פרסים: 33 שם: בראד פיט תאריך לידה: 18/12/1963 סרט מפורסם: "מר וגברת סמית" מספר פרסים: 63 להיות מועמדת לאוסקר להצטלם על השטיח האדום לככב במדורי רכילות כל אחד מהם יכול גם-כן לבצע את השיטות:
9
יש כאן מבנה יש מספר אובייקטים מאותו טיפוס (שחקן), בעלי תכונות זהות, אבל עם ערכים שונים אב טיפוס זה נקרא מחלקה (class) בדוגמא זו נקרא למחלקה בשם Actor שימו לב: כל שחקן הוא שונה אבל הם כולם אובייקטים (מופעים) של המחלקה Actor שם: בראד פיט תאריך לידה: 18/12/1963 סרט מפורסם: "מר וגברת סמית" מספר פרסים: 63 שם: רוברט רדפורד תאריך לידה: 18/08/1936 סרט מפורסם: "הצעה מגונה" מספר פרסים: 33
10
מהי מחלקה? מחלקה היא אבטיפוס לאובייקטים מאותו סוג (כמו struct)
לכל האובייקטים השייכים לאותה מחלקה יש את אותן תכונות, אך הם נבדלים בערכי התכונות כל האובייקטים מאותה מחלקה יודעים לבצע את אותן פעולות אובייקט של המחלקה יודע מה ערכי תכונותיו כלומר, אם נשאל את האובייקט "רוברט רדפורד" מה תאריך הלידה שלו, הוא ידע לענות אבל הוא לא ידע מה תאריך הלידה של אובייקט "בראד פיט"
11
תכונות ושיטות בתכנות מכוון עצמים
תכונה היא משתנה המהווה נתון מידע כחלק מאובייקט מסוים התכונה יכולה להיות מכל טיפוס שלמדנו עד כה (וטיפוסים נוספים) שיטה היא אוסף פקודות בשפה לביצוע פעולה בעלת רעיון משותף. כמו פונקציה, אך משויכת לאובייקט. למשל השיטה walkOnRedCarpet תדמה שהשחקן שהפעיל אותה הולך על השטיח האדום שם השיטה מעיד מה השיטה עושה (הרעיון המשותף של הפקודות) אוסף הפעולות בשיטה מעיד על ה"איך" השיטה מבצעת את הפעולה
12
מחלקות (classes) לעומת אובייקטים
מופעים של המחלקה Player 2: Attributes: Name = בראד פיט Birth Date = 18/12/1963 Famous movie = מר וגברת סמית Wins = 63 Methods: BeNominated WalkOnRedCarpet BeGossiped אובייקט Actor Attributes: Name Birth Date Famous movie Wins Methods: BeNominated WalkOnRedCarpet BeGossiped מחלקה (class) Player 1: Attributes: Name = רוברט רדפורד Birth Date = 18/03/1936 Famous movie = הצעה מגונה Wins = 33 Methods: BeNominated WalkOnRedCarpet BeGossiped אובייקט
13
מחלקות (classes) – שימו לב
מחלקה היא דרך מופשטת לתיאור של כל העצמים (אובייקטים) מאותו סוג עד שלא יצרנו אובייקט של המחלקה, יש לנו רק תיאור מופשט (אבטיפוס) Actor Attributes: Name Birth Date Famous movie Wins Methods: BeNominated WalkOnRedCarpet BeGossiped מחלקה (class)
14
שאלה הכיסא שאתם יושבים עליו, האם הוא מחלקה או אובייקט? והלוח?
זכרו: מחלקה היא רק תאור מופשט!
15
מהן תכונות ומהן שיטות ב- OOP
תכונה (attribute) היא משתנה המשויך לאובייקט מסוים התכונה יכולה להיות מכל טיפוס שלמדנו עד כה (וטיפוסים נוספים) שיטה (method) היא פונקציה, אך משויכת לאובייקט שם השיטה מעיד "מה" השיטה עושה אוסף הפעולות בשיטה מעיד על ה"איך" השיטה עושה זאת למחלקות שונות יתכנו תכונות ושיטות עם שמות זהים למשל, גם למחלקה "שחקן כדורסל" וגם למחלקה "ליצן" יכולה להיות התכונה name, ולשתיהן יכולה להיות השיטה show
16
OOP בשפת C++ כאשר נכתוב מחלקה נייצר 2 קבצים:
קובץ עם סיומת h שיכיל את ה"מה" של המחלקה: מה שמה מה תכונותיה מה הפעולות (שיטות, methods) קובץ עם סיומת cpp שיכיל את ה"איך" של המחלקה": מימוש הפעולות ניתן לכתוב את המימושים בקובץ ה- h, אך נעדיף להשאירו "נקי" עם תיאורי ה"מה" בלבד
17
דוגמה | המחלקה Clock clock.cpp clock.h
נשים לב שזוהי רק הגדרת המחלקה, עדיין לא יצרנו משתנה! יש לעשות include לקובץ ה- h של המחלקה #include <iostream> using namespace std; #include "clock.h" void Clock::show() { cout << (hours < 10 ? "0" : "") << hours << ":" << (minutes < 10 ? "0" : "") << minutes; } void Clock::tick() addMinutes(1); void Clock::addMinutes(int add) minutes += add; hours += minutes / 60; minutes %= 60; hours %= 24; clock.cpp במימוש יש לשייך את השיטה למחלקה, ע"י ציון: <class name>:: המילה class שמורה בשפה ומעידה שפה תהייה הגדרה של מחלקה #ifndef __CLOCK_H #define __CLOCK_H class Clock { public: int hours, minutes; void show(); void tick(); void addMinutes(int add); }; #endif // __CLOCK_H clock.h שם המחלקה נסביר בהמשך... תכונות המחלקה שיטות המחלקה לא לשכוח ; בסוף המחלקה
18
דוגמה | המחלקה Clock | שימוש ב- main
#include <iostream> using namespace std; #include "clock.h" void main() { Clock c; c.show(); cout << endl; c.hours = 22; c.minutes = 15; c.addMinutes(65); } כדי לפנות לתכונה או לשיטה של האובייקט נשתמש ב- "." לאובייקט המפעיל שיטה נקרא "אוביקט מפעיל" שם הטיפוס שם המשתנה קריאה לשיטה של האובייקט מתן ערכים לשדות האובייקט קריאה לשיטה של האובייקט המקבלת פרמטרים : 22:15 23:20
19
יצירת כמה אובייקטים מאותה מחלקה
#include <iostream> using namespace std; #include "clock.h" void main() { Clock c1, c2; c1.hours = 22; c1.minutes = 15; c2.hours = 14; c2.minutes = 45; cout << "Clock 1: "; c1.show(); cout << endl; cout << "Clock 2: "; c2.show(); } © Keren Kalif Clock 1: 22:15 Clock 2: 14:45 כאשר פונים לשיטה של אובייקט, האובייקט מכיר את ערכי תכונותיו שלו בלבד
20
בדיוק כמו עבודה עם מערך של מבנים..
יצירת מערך של Clock #include <iostream> using namespace std; #include "clock.h" void main() { Clock myClocks[2]; myClocks[0].hours = 22; myClocks[0].minutes = 15; myClocks[1].hours = 14; myClocks[1].minutes = 45; cout << "Clock 1: "; myClocks[0].show(); cout << endl; cout << "Clock 2: "; myClocks[1].show(); } בדיוק כמו עבודה עם מערך של מבנים.. Clock 1: 22:15 Clock 2: 14:45 © Keren Kalif
21
הקצאה דינאמית של Clock הקצאה דינאמית של מערך
#include <iostream> using namespace std; #include "clock.h" void main() { Clock* c = new Clock[2]; c[0].hours = 22; c[0].minutes = 30; c[1].hours = 20; c[1].minutes = 45; for (int i = 0; i < 2; i++) c[i].show(); cout << endl; } delete []c; הקצאה דינאמית של מערך #include <iostream> using namespace std; #include "clock.h" void main() { Clock* c = new Clock; c->hours = 22; c->minutes = 15; c->show(); cout << endl; delete c; } הקצאה דינאמית של אובייקט אחד כאשר המשתנה הוא מצביע, הפניה לשדות היא באמצעות <- שחרור המערך שחרור ההקצאה
22
הרשאות private ו- public
לא נרצה שהתכונות שלנו יהיו חשופות ושכל אחד יוכל לשנות את ערכיהן ע"י השמה למשל שלא יוכלו לשים בערך של הדקות מספר שאינו בין 0 ל- 59 לכן ניתן לתת הרשאות לתכונות ולשיטות של המחלקה לתכונות המחלקה ניתן הרשאת private, משמע ניתן לגשת אליהן רק מתוך המחלקה שיטות יהיו תחת הרשאת public כדי שיהיו נגישות מחוץ למחלקה שיטות שנרצה שיהיו לשימוש פנימי של המחלקה נגדיר ב- private #ifndef __CLOCK_H #define __CLOCK_H class Clock { private: int hours, minutes; public: void show(); void tick(); void addMinutes(int add); }; #endif // __CLOCK_H clock.h הרשאת ברירת המחדל היא private, לכן לא היינו חייבים לציין אותה בהתחלה. לכן חשוב היה בדוגמאות הקודמות לכתוב את ה- public
23
המחלקה Clock - השינוי בקוד
עם שינוי ההרשאה נקבל שגיאת קומפילציה: #include <iostream> using namespace std; #include "clock.h" void main1() { Clock c; c.hours = 22; c.minutes = 15; c.show(); cout << endl; }
24
הפתרון כדי לאפשר השמת ערך בתכונות שהן private, נכתוב שיטות שהן public המבצעות את פעולת ההשמה נרצה לכתוב שיטה המקבלת כנתון את הערך המבוקש, השיטה תבצע את בדיקות התקינות על ערך זה ולבסוף תשים אותו בתכונה נרצה לכתוב שיטה המחזירה את ערך התכונה נהוג לקרוא לשיטות אלו setter'ים ו- getter'ים © Keren Kalif
25
מימוש שיטות set ו- get תפקיד שיטת get להחזיר ערך של תכונה באובייקט
#include <iostream> using namespace std; #include "clock.h" int Clock::getMinutes() { return minutes; } int Clock::getHours() return hours; void Clock::setMinutes(int m) minutes = m; void Clock::setHours(int h) hours = h; void Clock::show() {...} void Clock::tick() {...} void Clock::addMinutes(int add) {...} מימוש שיטות set ו- get #ifndef __CLOCK_H #define __CLOCK_H class Clock { private: int hours, minutes; public: int getMinutes(); int getHours(); void setMinutes(int m); void setHours(int h); void show(); void tick(); void addMinutes(int add); }; #endif // __CLOCK_H תפקיד שיטת get להחזיר ערך של תכונה באובייקט תפקיד שיטת set לשים ערך בתוך תכונה באובייקט נשים לב שעדיין ניתן לשים ערכים לא תקינים בתכונות האובייקט. יטופל בהמשך!
26
שימוש בשיטות להשמה ולקבלת ערכים
#include <iostream> using namespace std; #include "clock.h" void main() { Clock c; c.show(); cout << endl; c.setHours(8); c.setMinutes(20); cout << "The hour is around " << c.getHours() << endl; } : 08:20 The hour is around 8
27
setter'ים ו- getter'ים – מימושים מתוקנים
#include "clock.h" int Clock::getMinutes() {...} int Clock::getHours() {...} void Clock::setMinutes(int m) { if (m < 0 || m >= 60) cout << "Minutes have to be between Value is unchanged.\n"; else minutes = m; } void Clock::setHours(int h) if (h < 0 || h >= 24) cout << "Hours have to be between Value is unchanged.\n"; hours = h; void Clock::show() {...} void Clock::tick() {...} void Clock::addMinutes(int add) {...} בתוך ה- setter'ים באה לידי ביטוי העבודה שהתכונות הן private: לא ניתן לשנות אותן ללא בקרה
28
תוצר ההרצה לאחר התיקון 0-858993460:0-858993460 08:20
#include <iostream> using namespace std; #include "clock.h" void main() { Clock c; c.show(); cout << endl; c.setHours(8); c.setMinutes(20); c.setHours(25); c.setMinutes(30); } תוצר ההרצה לאחר התיקון : 08:20 Hours have to be between Value is unchanged. 08:30
29
וכיצד ה- main ידע האם לקלוט נתונים מחדש?
במימוש הנוכחי בחרנו להדפיס למסך כאשר הקלט שהתקבל אינו תקין לכן ב- main אין לנו דרך לדעת האם פעולת ה- set בוצעה בהצלחה או האם נשאר הערך הקודם לכן נתקן את שיטות ה- set כך שיחזירו תשובה מסוג bool, כאינדיקציה להצלחה הפעולה
30
השיטות set: עדכון #ifndef __CLOCK_H #define __CLOCK_H class Clock {
bool Clock::setMinutes(int m) { if (m < 0 || m >= 60) cout << "Minutes … unchanged.\n"; return false; } else minutes = m; return true; #ifndef __CLOCK_H #define __CLOCK_H class Clock { private: int hours, minutes; public: int getMinutes(); int getHours(); bool setMinutes(int m); bool setHours(int h); void show(); void tick(); void addMinutes(int add); }; #endif // __CLOCK_H bool Clock::setHours(int h) { if (h < 0 || h >= 24) cout << "Hours … unchanged.\n"; return false; } else hours = h; return true;
31
שימוש בשיטות ה- set ב- main
Enter hours: 30 Hours have to be between Value is unchanged. Enter hours: 20 Enter minutes: 100 Minutes have to be between Value is unchanged. Enter minutes: 30 20:30 void main() { Clock c; bool res; int hours, minutes; do { cout << "Enter hours: "; cin >> hours; res = c.setHours(hours); } while (res != true); cout << "Enter minutes: "; cin >> minutes; res = c.setMinutes(minutes); c.show(); cout << endl; }
32
סיכום set'ים ו- get'ים כדי לאפשר השמת ערך בתכונה שהיא private, נכתוב שיטת set, שהיא public, המבצעת את פעולת ההשמה: השיטה תקבל כנתון את הערך המבוקש השיטה תבצע את בדיקות התקינות על ערך שהתקבל לבסוף השיטה תעדכן את הערך המתאים בתכונה כדי לאפשר קבלת ערך תכונה שהיא private, נכתוב שיטת get שהיא public נהוג לקרוא לשיטות אלו setter'ים ו- getter'ים
33
class לעומת struct על-מנת להגדיר טיפוס חדש: ההבדל ביניהם הוא בהרשאה:
בשפת C משתמשים ב- struct בשפת C++ משתמשים ב- class ההבדל ביניהם הוא בהרשאה: הרשאת ברירת המחדל ב- struct היא public ולכן יכולנו לפנות לשדותיו באופן ישיר הרשאת ברירת המחדל ב- class היא private ניתן לכתוב גם מתודות ב- struct אך זהו כבר ממש תכנות מונחה עצמים ולכן מקובל להשתמש ב- class
34
האם תמיד התכונות יהיו ב- private?
ישנם מקרים בהם אין צורך בבדיקות תקינות עבור הערכי התכונות למשל, עבור המחלקה Person המייצגת אדם, שתכונותיה הם שם ו- ת.ז., כאשר אין כרגע צורך בהגבלות תקינות #ifndef __PERSON_H #define __PERSON_H class Person { public: char name[20]; int id; }; #endif // __PERSON_H #include <iostream> using namespace std; #include "person.h" void main() { Person p; strcpy(p.name, "gogo"); p.id = 111; }
35
האם תמיד התכונות יהיו ב- private? (2)
אבל אם פתאום נחליט שמספר ת.ז. חייב להיות 9 ספרות, נצטרך לעדכן את המחלקה, וה- main כבר לא יתקמפל! bool Person::setId(int i) { int counter = 0; while (i > 0) i /= 10; counter++; } if (counter == 9) id = i; return true; else return false; #ifndef __PERSON_H #define __PERSON_H class Person { public: char name[20]; private: int id; int getId(); bool setId(int i); }; #endif // __PERSON_H void main() { Person p; strcpy(p.name, "gogo"); p.id = 111; } int Person::getId() { return id; }
36
תכונות תמיד יהיו private!
אפילו אם בעת כתיבת המחלקה אין הגבלות תקינות על תכונה, תמיד נשים אותה עם הרשאת private ונספק מתודות set ו- get 2 סיבות מרכזיות לאופן כתיבה זה: שאם בעתיד תתווסף בדיקת תקינות, מי שמשתמש במחלקה לא יצטרך לשנות את הקוד שלו, שכן כבר מראש הוא ישתמש במתודת ה- set, וכל שינוי בתוך גוף המתודה יהיה שקוף מבחינתו אחידות, כך שהפניה לכל התכונות במחלקה תהייה באופן זהה, באמצעות שיטות set או get
37
אתחול תכונות שהן private
בהמשך נראה כיצד ניתן לתת ערכים לתכונות האובייקט עם אתחולו כרגע עם יצירת האובייקט כל ערכי שדותיו הם זבל נקפיד שתכונות האובייקט יהיו private!
38
זוהי גם העמסת שיטות, שכן עכשיו ניתן לקרוא לשיטות אלו ב- 2 אופנים
ערכי default לפרמטרים #ifndef __CLOCK_H #define __CLOCK_H class Clock { private: int hours, minutes; public: int getMinutes(); int getHours(); bool setMinutes(int m=0); bool setHours (int h=0); void show(); void tick(); void addMinutes(int add); }; #endif // __CLOCK_H clock.h #include <iostream> using namespace std; #include "clock.h" void main() { Clock c; c.show(); cout << endl; c.setHours(5); c.setMinutes(); } זוהי גם העמסת שיטות, שכן עכשיו ניתן לקרוא לשיטות אלו ב- 2 אופנים : 05:00
39
נגדיר את ה- enum ב- public כדי שיהיה נגיש גם מחוץ למחלקה
class Clock { public: enum eDisplayType {FULL_DAY, HALF_DAY}; int getMinutes(); int getHours(); eDisplayType getDisplayType(); bool setMinutes(int m=0); bool setHours(int h=0); void setDisplayType(eDisplayType type); void show(); void tick(); void addMinutes(int add); private: int hours, minutes; eDisplayType displayType; }; נגדיר את ה- enum ב- public כדי שיהיה נגיש גם מחוץ למחלקה void Clock::show() { int h = hours; if (displayType == HALF_DAY) h %= 12; cout << (h < 10 ? "0" : "") << h << ":" << (minutes < 10 ? "0" : "") << minutes; cout << (hours < 12 ? "am" : "pm"); } ה- private עבר לסוף הקובץ משום שיש בו שימוש ב- enum, שצריך להיות מוגדר מעליו
40
שימוש ב- enum במחלקה: ה- main
void main() { Clock c; c.setHours(22); c.setMinutes(30); c.setDisplayType(Clock::FULL_DAY); cout << "The time is (full day): "; c.show(); cout << endl; c.setDisplayType(Clock::HALF_DAY); cout << "The time is (half day): "; } ה- enum הוא קבוע המוגדר בחלק ה- public במחלקה, לכן ניתן לגשת אליו בשמו המלא מחוץ למחלקה The time is (full day): 22:30 The time is (half day): 10:30pm
41
מחלקה המכילה מחלקה #ifndef __OVEN_H #define __OVEN_H
#include "clock.h" class Oven { private: int temperature; Clock startTime; int minutesToWork; public: void show(); int getTemperature(); Clock getStartTime(); int getMinutesToWork(); void setTemperature(int t); void setStarTime(Clock c); void setMinutesToWork(int t); }; #endif // __OVEN_H void Oven::show() { cout << "Temperature: " << temperature << "\nMinutes: " << minutesToWork << "\nStart working at "; startTime.show(); cout << endl; }
42
מחלקה המכילה מחלקה: ה- main
#include <iostream> using namespace std; #include "clock.h" #include "oven.h" void main() { Oven o; Clock c; c.setHours(13); c.setMinutes(); o.setTemperature(180); o.setMinutesToWork(50); o.setStarTime(c); o.show(); } Temperature: 180 Minutes: 50 Start working at 13:00
43
תזכורת: בעיית ה- include הכפולים
#include <iostream> using namespace std; #include "clock.h" #include "oven.h" void main() { Oven o; Clock c; c.setHours(13); c.setMinutes(); o.setTemperature(180); o.setMinutesToWork(50); o.setStarTime(c); o.show(); } במידה והקובץ clock.h לא היה עטוף ב- ifndef היינו מקבלים שגיאת קומפילציה של redefinition
44
שדרוג המחלקה Oven #ifndef __OVEN_H #define __OVEN_H #include "clock.h" class Oven { private: int temperature; Clock startTime; int minutesToWork; public: void show(); int getTemperature(); Clock& getStartTime(); int getMinutesToWork(); void setTemperature(int t); void setStarTime(Clock& c); void setMinutesToWork(int t); }; #endif // __OVEN_H פרמטרים מטיפוס אובייקט תמיד ישלחו by ref מטעמי יעילות, אין שינוי במימוש. ערך מוחזר המחזיר תכונה של האובייקט שהינה אובייקט בעצמה, גם תחזור by ref מטעמי יעילות, כדי לחסוך את ההעתקה מטעמי יעילות, כדי לא לשלוח העתק של האובייקט לפונקציה ומהפונקציה, נקבל ונשלח רק reference אליו. מקביל לשליחת מצביע ב- C
45
הבעייתיות בהעברת פרמטר by ref
הבעיה: האובייקט המקורי חשוף לשינויים בתוך הפונקציה הפתרון: הצהרה שהפונקציה אינה משנה את הפרמטר סינטקס: שמים את המילה const לפני טיפוס הפרמטר: void foo(const MyClass& c) המשתנה יהיה קבוע בתוך הפונקציה, ולא ניתן יהיה לשנותו
46
הגנה על הפרמטר מפני שינויים בתוך הפונקציה:
העברת פרמטר כ- const #ifndef __OVEN_H #define __OVEN_H #include "clock.h" class Oven { private: int temperature; Clock startTime; int minutesToWork; public: void show(); int getTemperature(); Clock& getStartTime(); int getMinutesToWork(); void setTemperature(int t); void setStarTime(const Clock& c); void setMinutesToWork(int t); }; #endif // __OVEN_H כאשר מעבירים פרמטר לפונקציה שהוא אובייקט תמיד נעביר אותו by ref מטעמי יעילות, ונציין שהוא const, כדי שיהיה ברור לקורא הקוד שהפרמטר הועבר by ref מטעמי יעילות, ולא על-מנת לשנותו הגנה על הפרמטר מפני שינויים בתוך הפונקציה: מועבר by ref מטעמי יעילות – לחסוך את ההעתקה, ולכן ישנו ה- const כדי ליידע את הקורא שמשתנה זה אינו ניתן לשינוי בפונקציה
47
משתנים/פרמטרים שהם const
void main() { Clock c1; c1.show(); const Clock c2; c2.show(); }
48
כאשר שיטה אינה משנה את ערכי תכונות האובייקט, נצהיר עליה כ- const
class Clock { public: enum eDisplayType {FULL_DAY, HALF_DAY}; int getMinutes() const; int getHours() const; eDisplayType getDisplayType() const; bool setMinutes(int m=0); bool setHours(int h=0); void setDisplayType(eDisplayType type); void show() const; void tick(); void addMinutes(int add); private: int hours, minutes; eDisplayType displayType; }; שימו לב: הקומפיילר אינו מתריע על אי הגדרת const, אך זהו תכנות נכון, מעין "חוזה" בין מי שכותב את המחלקה למי שמשתמש בה, ולכן יש להקפיד על שימוש נכון ב- const
49
const הוא חלק מחתימת השיטה
2 שיטות בעלות שם זהה ורשימת פרמטרים זהה, יכולות להיבדל אחת מהשניה ב- const (functions overloading): במקרה זה, כאשר יש משתנה רגיל ומשתנה const כל אחד יפנה לשיטה המתאימה
50
שאלה האם ניתן היה לוותר על אחת מ- 2 הגרסאות? כן, הגרסא בלי ה- const
משתנה שהוא const יכול להפעיל אך ורק שיטה שהיא const משתנה רגיל יכול להפעיל כל שיטה
51
האם הקוד הבא מתקמפל? אם כן, מה הפלט, אחרת מהי השגיאה?
התיקון יהיה להגדיר את השיטה foo כ- const הקוד אינו מתקמפל מאחר ו- getInner מחזירה אובייקט שהוא const ולכן ניתן להפעיל עליו רק שיטות שהוגדרו כ- const
52
פונקציית constexpr ידוע לנו כי ערך החוזר פונקציה מחושב בזמן ריצה
במידה וכל הערכים שבשימוש הפונקציה ידועים בזמן קומפילציה, ניתן לגרום לפונקציה לחשב את הערך המוחזר כבר בזמן קומפילציה ובכך לחסוך בביצועים בזמן ריצה constexpr double getCircleArea(int radius) { return 3.14 * radius * radius; } void main() const int RADIUS = 5; cout << "circle area with radius=" << RADIUS << " is " << getCircleArea(RADIUS) << endl; int radius; cout << "Enter radius: "; cin >> radius; cout << "circle area with radius=" << radius << " is " << getCircleArea(radius) << endl; מאחר ו- r1 הוא const, הקומפיילר מחשב את הערך המוחזר מהפונקציה כבר בזמן קומפילציה מאחר וערכו של r2 ידוע רק בזמן ריצה, הפונקציה מתנהגת רגיל ומחושבת בזמן ריצה
53
פונקציות constexpr כאשר הקומפיילר מתייחס להגדרת ה- constexpr הפונקציה הופכת להיות inline מתודות המוגדרות כ- constexpr אינן יכולות להיות וירטואליות (רלוונטי לפרק של פולימורפיזם) ישנה הגבלה ל- return יחיד במימוש, אחרת תהיה שגיאת קומפילציה
54
כיצד ניתן לדעת שהקומפיילר אכן משתמש בפונקציה כ- constexpr?
constexpr double getCircleArea(int radius) { return 3.14 * radius * radius; } void main() const int RADIUS = 5; cout << "circle area with radius=" << RADIUS << " is " << getCircleArea(RADIUS ) << endl; static_assert(getCircleArea(RADIUS ) == (3.14 * 25) , "result should be 3.14*5*5"); int radius; cout << "Enter radius: "; cin >> radius; cout << "circle area with radius=" << radius << " is " << getCircleArea(radius) << endl; static_assert(getCircleArea(radius) == (3.14 * 25), static_assert משמש לבדיקת ערך הידוע בזמן קומפילציה. במידה והערך אינו ידוע, תוצג שגיאה בזמן קומפילציה.
55
משתנה constexpr לעומת משתנה const
void main() { int x; cin >> x; const int y1 = x + 5; constexpr int y2 = x + 5; }
56
מתודות inline כאשר כותבים מתודה במחלקה, הקפיצה אליה מתבצעת בזמן ריצה (זהו המנגנון הרגיל של קריאה לפונקציות) קריאות מרובות למתודה יכולות לייצר תקורה בזמן ריצת התוכנית תקורה זו די מינימלית, אבל יש מערכות בהן כל חלקיק שניה משמעותי הגדרת המתודה כ- inline תדאג למנוע את הקפיצה למתודה ע"י שתילת קוד המתודה במקום המבוקש (הרעיון כמו macro) ציון כי מתודה היא inline הינה המלצה בלבד לקומפיילר: הקומפיילר יכול להתעלם ממנה הקומפיילר יכול להחליט על מתודה מסויימת שהיא inline גם אם המתכנת לא הצהיר על כך במפורש
57
מתודת inline: דוגמה class Stam { private: int x; public: Stam(int num)
x = num; } inline int getX() const; }; int Stam::getX() const return x; המימוש של פונקציית inline צריך להיות זמין בזמן הקומפילציה, ולכן ימומש בקובץ ה- h int getX() const {return x;} מתודות שממומשת ב- header הינן עם המלצת inline באופן אוטומטי. פונקציות הכוללות לולאות לא יהיו inline.
58
ביחידה זו למדנו: מהי מחלקה פניה אל תכונות ושיטות המחלקה הרשאות גישה
מחלקה המכילה מחלקה אחרת העברת פרמטר by ref בעיית ה- include הכפולים פרמטר const שיטת const ערך מוחזר const משתנה const constexpr מתודות inline
59
תרגול (1/4) כתוב את המחלקה Survivor שנתוניה: שיטות המחלקה:
שם השורד: מחרוזת סטטית בגודל 20 גיל סטטוס משפחתי (רווק/נשוי/במערכת יחסים) שיטות המחלקה: אתחול: שיטה המקבלת מחרוזת המייצגת את שם השורד, גיל, וסטטוס משפחתי ומאתחלת את נתוניו שיטה המדפיסה את נתוני השורד
60
תרגול (2/4) כתבו main: שאלו את המשתמש כמה שורדים יש בכל שבט
הקצו דינאמית 2 מערכים של מצביעים ל- Survivor בגודל המבוקש קראו נתונים בלולאה לתוך 2 מערכי השורדים (זכרו: יש להקצות דינאמית כל איבר. מדוע?) הדפיסו את נתוני 2 המערכים שחררו זכרון
61
תרגול (3/4) כתבו את המחלקה Tribe שנתוניה: שיטות המחלקה:
שם השבט: מחרוזת סטטית בגודל 20 מספר השורדים המקסימלי בשבט מערך של מצביעים ל- Survivor מספר השורדים שנשארו בשבט שיטות המחלקה: אתחול (תקבל שם שם השבט, ומספר השורדים המקסימלי, ותאתחל את מערך השורדים ואת מספר השורדים שנותרו בהתאם). הוספת שורד לשבט ועדכון הנתונים הרלוונטים. הדחה: תקבל שם של שורד. ותשחרר את הקצאתו מהמערך, תצמצם את הרווח שנותר ותעדכן את מספר השורדים שנותרו בשבט הדפסה שמות כל השורדים בשבט
62
תרגול (4/4) שנו את ה- main: ייצר 2 שבטים
קרא נתונים בלולאה לתוך 2 השבטים (זכרו: יש להקצות דינאמית כל איבר) הדפיסו את השבטים הדיחו 2 שורדים הדפיסו את השבטים לאחר ההדחות שחררו זיכרון solution
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.