Presentation is loading. Please wait.

Presentation is loading. Please wait.

תכנות מכוון עצמים ו- C++ יחידה 02 העמסת פונקציות, ערכי ברירת מחדל, enum, קימפול מותנה קרן כליף.

Similar presentations


Presentation on theme: "תכנות מכוון עצמים ו- C++ יחידה 02 העמסת פונקציות, ערכי ברירת מחדל, enum, קימפול מותנה קרן כליף."— Presentation transcript:

1 תכנות מכוון עצמים ו- C++ יחידה 02 העמסת פונקציות, ערכי ברירת מחדל, enum, קימפול מותנה
קרן כליף

2 נכון שזה נכון?? אז די!

3 ביחידה זו נלמד: ranged based for loop auto string literals
העמסת פונקציות ערכי ברירת מחדל לפונקציות enum תזכורת לבעיית ה- include'ים כפולים

4 Ranged Based For-Loop ניתן לרוץ על לולאה ללא אינדקס! C++11 1 2 3 4 5
void main() { int arr[] = { 1,2,3,4,5 }; for (int x : arr) cout << x << " "; cout << endl; for (int& x : arr) x = x*x; } חסרון: לולאה זו מתאימה רק למערכים עליהם רוצים לעבור באופן סדרתי על כל האיברים מההתחלה לסוף (אי אפשר למשל לעבור רק על מיקומים זוגיים וכד')

5 על משתנה מטיפוס זה ניתן להפעיל את הפונקציה name() שמחזירה את שם הטיפוס
auto C++11 ניתן להגדיר משתנים ללא טיפוס ספציפי! typeid היא פונקציה המקבלת משתנה או טיפוס ומחזירה משתנה מטיפוס type_info, המחזיק מידע על טיפוס. על משתנה מטיפוס זה ניתן להפעיל את הפונקציה name() שמחזירה את שם הטיפוס void main() { auto a = 5; auto* b = &a; auto c = &a; auto& d = a; auto x; cout << typeid(a).name() << endl; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(d).name() << endl; d = 3; cout << a << " " << d << endl; char ch = 'a'; c = ch; } טיפוסו של המשתנה נקבע מיד עם אתחולו ולכן חייב להיות ידוע בזמן קומפילציה int int * 3 3 // cannot convert from 'char' to 'int *'

6 auto + ranged for loop void main() {
char* daysOfWeek[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; for (auto day : daysOfWeek) cout << day << " "; cout << endl; }

7 Raw String Literals C++11 כדי להציג למסך תווים מיוחדים (גרשיים, גרש, סלש וכד') יש לשים לפניהם \, מה שלפעמים יכול להיות מעיק, וכן להציג טקסט עם ירידות שורה הפתרון הוא Raw String Literals! void main() { char* s1 = "\"Hello World\""; cout << s1 << endl; char* s2 = R"("Hello World")"; cout << s2 << endl; char* s3 = "\"foo()\""; cout << s3 << endl; char* s4 = R"("foo()")"; char* s4 = R"##("foo()")##"; cout << s4 << endl; } נעטוף את הטקסט שנרצה שיוצג ב- R”(The Text)” "Hello World" "foo()" הבעיה: יש את הרצף "( בתוך הטקסט הפתרון: לעטוף את הסוגריים ברצף כלשהו לבחירתכם (פה בחרתי ##)

8 העמסת פונקציות (Functions Overloading)
כלומר, ניתן להגדיר פונקציות דומות עם שם זהה, המקבלות טיפוסים שונים דוגמה: void swap(int& a, int& b) { int temp = a; a = b; b = temp; void swap(char& a, char& b) char temp = a; void main() } int n1=3, n2=6; swap(n1, n2); char ch1='a', ch2='z'; swap(ch1, ch2); {

9 בעית דו-משמעות (ambiguity)
הקומפיילר אינו יכול להבדיל בין 2 פונקציות הנבדלות אך ורק בערך המוחזר הסיבה: לא בהכרח יהיה שימוש בערך המוחזר דוגמה: int foo() {return 0;} char foo() {return 'a';} void main() { foo(); הקומפיילר לא ידע לאיזו גרסא לפנות, ונקבל שגיאת ambiguity: error C2371: 'foo' : redefinition; different basic types

10 בעית דו-משמעות (ambiguity) (2)
גם במקרה זה הקומפיילר לא ידע לאיזו גרסה לפנות: void foo(double d) {} void foo(float f) {} void main() { double d = 5.2; float f = 7.3f; foo(d); foo(f); } void foo(double d) {} void foo(float f) {} void main() { int x=3; foo(x); דוגמא תקינה error C2668: 'foo' : ambiguous call to overloaded function הקומפיילר לא יודע אם להמיר את x ל- double או ל- float בעיית ה- ambiguity אינה בעיה של הפונקציות, אלא של אופן הקריאה, שאינו מורה לקומפיילר באופן חד-משמעי לאיזו גרסא לפנות

11 ערכי ברירת מחדל (default values)
בשפת C++ ניתן לתת ערך ברירת מחדל לפרמטרים האחרונים שהפונקציה מקבלת, ובכך יהיה ניתן בשימוש להחסיר ארגומנטים אלו התוצאה היא העמסת הפונקציה void printChar(char ch='*', int times=5); void main() { printChar('#', 3); printChar(); void printChar(char ch, int times) } for (int i=0 ; i < times ; i++) cout << ch; cout << endl; במקרה של הפרדה בין ההצהרה למימוש, ערכי ברירת המחדל יכתבו בהצהרה העמסת 3 פונקציות במחיר מימוש של אחת 

12 ערכי ברירת מחדל (2) void printChar(char ch='*', int times=5);
void printChar(int times, char ch='*'); void main() { printChar('#', 3); printChar(); printChar(8); void printChar(char ch, int times) } for (int i=0 ; i < times ; i++) cout << ch; cout << endl; void printChar(int times, char ch) printChar(ch, times); נשים לב כי לא ניתן לתת ערך ב"מ לפרמטר times, משום שנקבל שגיאת ambiguity במידה ויקראו לפונקציה ללא פרמטרים קריאה לפונקציה הראשונה קריאה לפונקציה השניה לא ניתן היה להגדיר את את הפונקציה הבאה: void printChar(int times=5, char ch); משום שערכי ב"מ ניתן לתת רק לפרמטרים האחרונים ברשימה. אחרת, הקריאה לפונקציה תצטרך להיות: printChar( , ‘*’) וזה לא תקין

13 enum הגדרת טיפוס חדש שיכיל ערך מספרי מתוך קבוצה מוגדרת מראש
כלומר, הגדרת אוסף קבועים בעלי קשר לוגי למשל: ימות השבוע, אוסף צבעים, ערכים בוליאנים וכד' דוגמה: enum eColor {RED, YELLOW, BLUE}; זוהי הגדרה של 3 קבועים, הראשון מקבל באופן אטומטי את ערך 0, זה שאחריו את ערך 1 וכו'. כעת נוכל להגדיר בתוכנית משתנים מטיפוס color ולתת להם את הערכים RED/YELLOW/BLUE

14 enum - מתן ערך שגוי אם ניתן למשתנה מטיפוס ה- enum ערך מספרי שאינו הוגדר בקבוצת הערכים שלו, נראה את הערך המספרי (לא נקבל שגיאה) קליטה לתוך enum צריכה להתבצע לתוך int ואז לבצע המרה enum eColor {RED, YELLOW, BLUE}; void main() { eColor c1 = RED; cout << "c1=" << c1 << endl; eColor c2 = (eColor)5; cout << "c2=" << c2 << endl; eColor c3; //cin >> c3; int colorNum; cin >> colorNum; c3 = (eColor)colorNum; } הקומפיילר לא מבצע casting מ- int לטיפוס החדש, לכן צריך לעשות המרה מפורשת

15 טריק לקבלת שמו של enum ניתן להגדיר מערך גלובלי של מחרוזות עם שמות ה- enum בהתאמה לערכיהם enum eColor { White, Black, Red, Yellow, Blue }; const char* colors[] = { "White", "Black", "Red", "Yellow", "Blue" }; void main() { eColor c = Red; cout << "Selected color is " << colors[c] << endl; }

16 מאחר ולא נתנו ל- Red ערך, ערכו יהיה ערך עוקב לקבוע שלפניו
enum – מתן ערכים שונים ניתן לכל ערך באוסף לתת ערך שאינו עוקב לערך שלפניו, ע"י השמה: מאחר ולא נתנו ל- Red ערך, ערכו יהיה ערך עוקב לקבוע שלפניו enum eColor { White = 10, Black = 20, Red, Yellow = 40, Blue = 50 }; void main() { eColor c1 = (eColor)1, c2 = (eColor)20, c3 = (eColor)21, c4 = (eColor)45; }

17 Enhanced Enums enum eColor { Red, Yellow, Green };
enum eGrades:char {A, B, C}; //enum eGrades:char { 'A', 'B', 'C' }; void main() { eColor c1 = Red; eColor c2 = eColor::Red; char mathGrade = eGrades::A; cout << "Math grade is " << mathGrade << endl; char physicsGrade = eGrades::B + 'A'; cout << "Physics grade is " << physicsGrade << endl; } הערכים תמיד צריכים להיות מאותחלים כמספרים ניתן (ועדיף!) לפנות לערך בקבוצה עם שם הטיפוס. יותר קריא! למרות שהגדרנו את הטיפוס כ- char, הערכים סדרתיים ומתחילים מ- 0 Math grade is Physics grade is B

18 Enhanced Enums - הפתרון
enum eGrades:char { A='A' , B, C}; void main() { char mathGrade = eGrades::A; cout << "Math grade is " << mathGrade << endl; char physicsGrade = eGrades::B/* + 'A'*/; cout << "Physics grade is " << physicsGrade << endl; } Math grade is A Physics grade is B

19 Enum Classes ניתן להגדיר את ה- enum כ- class, ואז חלות עליו מספר הגבלות enum class eSeason {Winter, Spring, Summer, Fall }; void main() { int s1 = eSeason::Summer; int s2 = (int)eSeason::Winter; int s3 = Fall; } אין המרה אוטומטית ל- int הפניה לערך חייבת להיות לכלול את שם המחלקה

20 תזכורת: פעולת ה- include
פעולת ה- include היא פקודת קדם-מעבד (preprocessor) אשר שותלת בקוד במקום כל פקודת include את תוכן הקובץ שאותו כללנו בפקודה a.h struct A { int x; }; struct A { int x; }; void main() } main.cpp #include "a.h" void main() } {

21 הבעיתיות בפקודת include
a.h struct A { int x; }; struct A { int x; }; // prototypes void foo(); void main() } נקבל שגיאה של redefinition מאחר והקומפיילר רואה את ההצהרה על המבנה שמוגדר ב- a.h יותר מפעם אחת b.h #include "a.h" // prototypes void foo(); main.cpp #include "a.h" #include "b.h" void main() } {

22 הפתרון: הידור מותנה ראינו בעבר את הפקודה #define לצורך הגדרת קבוע מסוים פקודה זו מוסיפה את הקבוע שהוגדר לטבלת סימולים של התוכנית במידה וטרם הוגדר. במידה וכבר הוגדר דורסת את ערכו. ניתן גם לכתוב פקודת define ללא ערך, רק כדי להכניס קבוע מסוים לטבלת הסימולים ניתן לבדוק האם קבוע מסוים הוגדר בטבלת הסימולים בעזרת הפקודה #ifdef או אם לא הוגדר בעזרת הפקודה #ifndef במידה והתנאי מתקיים, הקופיילר יהדר את קטע הקוד הבא עד אשר יתקל ב- #endif

23 כעת יש לנו ב- main פעם אחת בלבד את ההגדרות מכל קובץ
הפתרון עם הידור מותנה a.h b.h #ifndef __A_H #define __A_H struct A { int x; }; #endif // __A_H #ifndef __B_H #define __B_H #include "a.h" // prototypes void foo(); #endif // __B_H main.c לאחר preprocessor struct A { int x; }; // prototypes void foo(); void main() } foo(); כעת יש לנו ב- main פעם אחת בלבד את ההגדרות מכל קובץ main.cpp #include "a.h" #include "b.h" void main() } foo(); { טבלת הסימולים: __A_H __B_H

24 הקומפיילר אינו מכיר את הטיפוס A ולכן שגיאת הקומפילציה...
בעיה נוספת ב- include כאשר יש 2 מבנים אשר כל אחד מגדיר אובייקט מטיפוס המבנה השני, מתקבלת שגיאת קומפילציה שקשה להבינה: #ifndef __A_H #define __A_H #include "b.h" struct A { B b; }; #endif // __A_H #ifndef __B_H #define __B_H #include "a.h" struct B { A a; }; #endif // __B_H הקומפיילר אינו מכיר את הטיפוס A ולכן שגיאת הקומפילציה... #include "a.h" #include "b.h" void main() } { טבלת הסימולים: __A_H __B_H

25 הצהרה שהמחלקה תוגדר בהמשך
הפתרון במקרה זה נדאג שלפחות אחד המבנים יכיל רק מצביע למבנה השני, ולא אובייקט כאשר יוצרים אובייקט צריך לבצע include לקובץ המגדיר אותו כאשר יש מצביע לאובייקט לא חייבים לבצע include לקובץ המכיל אותו, אלא להסתפק בהצהרה שמבנה זה יוגדר בהמשך בקובץ cpp בו תהיה היצירה של האובייקט נבצע את ה- include לקובץ בו מוגדר המבנה #ifndef __A_H #define __A_H #include "b.h" struct A { B b; }; #endif // __A_H #ifndef __B_H #define __B_H struct A; struct B { A* a; }; #endif // __B_H הצהרה שהמחלקה תוגדר בהמשך

26 ביחידה זו למדנו: ranged based for loop auto string literals
העמסת פונקציות ערכי ברירת מחדל לפונקציות enum תזכורת לבעיית ה- include'ים כפולים


Download ppt "תכנות מכוון עצמים ו- C++ יחידה 02 העמסת פונקציות, ערכי ברירת מחדל, enum, קימפול מותנה קרן כליף."

Similar presentations


Ads by Google