Download presentation
Presentation is loading. Please wait.
1
תרגול 10: הכרות עם ++C היכרות עם ++C
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C תרגול 8 היכרות עם ++C
2
C++ שפת תכנות שנבנתה מעל שפת C
עד כדי מספר הבדלים קטן C99 - סטנדרט של C, שאימץ כמה מהתכונות של C++ הקומפיילר של C++ שאנחנו נשתמש בקורס הוא g++ לקבצי C++ נהוג לתת סיומות .h לקובץ header, כמו ב-C .cpp לקובץ מימוש (source file)
3
הערות ב C++ // ועד סוף השורה int foo(int n) { // this is a C++ comment
ניתן להשתמש גם ב-C-style הערות /* ... */ int foo(int n) { // this is a C++ comment int i = 7; // this is another C++ comment /* this is a C style comment */ return n+i; }
4
עדיף להכריז על משתנים כמה שיותר קרוב לשימוש הראשון שלהם
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C הגדרת משתנים ב C++ ב C++ ניתן להגדיר משתנים לא רק בתחילת בלוק גם ב-C99 int foo(int size){ ... for(int i = 0; i < size; ++i) SetResult setResult = setCreate(...); ListResult listResult = listCreate(...); { C++/C99 int foo(int size){ int i; SetResult setResult; ListResult listResult; ... for(i = 0; i < size; ++i) ... setResult = setCreate(...); listResult = listCreate(...); { C before C99 gcc בלי -pedantic מרשה להגדיר משתנים לא בתחילת בלוק גם לפני c99 עדיף להכריז על משתנים כמה שיותר קרוב לשימוש הראשון שלהם
5
זיכרון דינמי ב C++: הקצאת עצמים בודדים
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C זיכרון דינמי ב C++: הקצאת עצמים בודדים C++ new type; delete pointer; int *pi = new int ; *pi = 6; ... delete pi; int *pi = (int*)malloc(sizeof(int)) ; if(pi == NULL){ ... /* out of memory */ } *pi = 6; ... free(pi); C malloc(sizeof(type)); free(pointer); אין צורך בבדיקת תוצאה של new - במקרה של כישלון נזרק exception הקומפיילר של C לא דורש casting אחרי ,malloc הקומפיילר של ++C כן דורש אין צורך בבדיקת תוצאה של new אין צורך ב-casting
6
זיכרון דינמי ב C++: הקצאת מערכים
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C זיכרון דינמי ב C++: הקצאת מערכים int elements_number = ...; int *pi = new int[elements_number]; pi[3] = 6; ... delete[] pi; C++ new type[#elements]; delete[] pointer; int elements_number = ...; int *pi = (int*)malloc(sizeof(int) * elements_number) ; if(pi == NULL){ ... /* out of memory */ } pi[3] = 6; ... free(pi); C malloc(sizeof(type)*#elements); free(pointer);
7
זיכרון דינמי ב C++ ב C++ מותר אך לא נהוג להשתמש בפונקציות free/malloc
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C זיכרון דינמי ב C++ ב C++ מותר אך לא נהוג להשתמש בפונקציות free/malloc בכל מקרה, אסור לערבב בין new/delete ו-free/malloc אסור להפעיל free על הזיכרון שהוקצה על ידי new אסור להפעיל delete על הזיכרון שהוקצה על ידי malloc delete אחרי new[] יכול לעבוד במקרה בהקצאה של טיפוסים פרימיטיביים, אך זה עדיין תיכנות רע:
8
זיכרון דינמי ב C++ חייבים להתאים בין סוגי new ו-delete
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C זיכרון דינמי ב C++ חייבים להתאים בין סוגי new ו-delete delete אחרי new delete[] אחרי new[] מותר להפעיל delete על NULL if(ptr != NULL) { delete ptr; } delete אחרי new[] יכול לעבוד ***במקרה*** בהקצאה של טיפוסים פרימיטיביים, אך זה עדיין תיכנות רע:
9
const ב C++ const type קיימים גם ב-C99
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C const ב C++ תוספת לשם טיפוס, מציינת טיפוס של קבוע const type משתנים מסוג const חייבים לאתחל לא ניתן לבצע השמה אליהם קיימים גם ב-C99 const int maxSize = 120; C++/C99 #define MAX_SIZE 120 C before C99
10
שימו לב להבדל בין אתחול להשמה
const ב C++ const int i = 7; // OK. initialization to 7 i = 17; // error: assignment to a const variable const int j; // error: definition of a const variable // without initialization int k = i; // OK. assignment of a const variable to // a non const variable const int intArray[] = { 7, 17, 27, 777 }; intArray[2] = 8; // error: assignment to a const שימו לב להבדל בין אתחול להשמה
11
מצביעים ל-const ב C++ ניתן להגדיר מצביעים לטיפוסים קבועים const type*
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C מצביעים ל-const ב C++ ניתן להגדיר מצביעים לטיפוסים קבועים const type* למשל, const int* pi; pi הוא משתנה מסוג מצביע ל- const int יכול להחזיק כתובות של משתנים מסוג const int ניתן לבצע השמה של כתובת של משתנה מטיפוס type לתוך מצביע מטיפוס const type* בפרט, ניתן להמיר int* לconst int*- אך לא להפך למתקדמים: const int* pi; // pointer to const int int const* pi; // pointer to const int int* const pi; // const pointer to int const int* const pi; // const pointer to const int
12
מצביעים ל-const ב C++ באילו ביטויים יש שגיאה ? const int i = 7;
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C מצביעים ל-const ב C++ באילו ביטויים יש שגיאה ? const int i = 7; const int* cpi = &i; *cpi = 17; int j = 17; int* pi = &j; cpi = &j; cpi= pi; pi = cpi; השגיאות ב- *cpi = 17; pi = cpi;
13
שימוש ב-const בפונקציות: פרמטרים
חשוב מאוד עבור פרמטרים שהם מצביעים int foo(int* i, const char* c, float*f, const double* d); C++/C99 /* this function changes values pointed by i,f does not change values pointed by c,d */ int foo(int* i, char* c, float*f, double* d); C before C99 חוסך תיעוד הקומפיילר מוודא שלא משנים פרמטרים שהם const
14
שימוש ב-const בפונקציות: ערכי חזרה
חשוב מאוד עבור ערכי חזרה שהם מצביעים const char* foo(int i); C++/C99 /* please do not change the returned string !!! */ char* foo(int i); C before C99 חוסך תיעוד הקומפיילר מוודא שלא משנים ערכי חזרה שהם const
15
Reference reference הוא שם נוסף למשתנה קיים לדוגמה:
type& <reference name> = <referenced variable>; reference הוא שם נוסף למשתנה קיים לדוגמה: הביטוי &int משמעותו reference לטיפוס int לא לבלבל עם &x שמשמעותו "הכתובת של משתנה "x כל פעולה שמפעילים על reference, בפועל מתבצעת על המשתנה שה-reference מתייחס אליו int i = 7; int &j = i;
16
שימו לב: ההשמה מתבצעת ל-i
Reference reference חייב להיות מאותחל לאחר האתחול לא ניתן לשנות את המשתנה שה-reference מתייחס אליו int i = 8 ; int &j = i; int x = i; i = 5 ; // i = j = 5 , x = 8 j++; // i = j = 6, x = 8 j = x; // i = j = 8, x = 8 j = 7; // i = j = 7, x = 8 שימו לב: ההשמה מתבצעת ל-i j ממשיך להתייחס ל-i
17
Reference העברת פרמטרים לפונקציות/ החזרת ערכי חזרה
by value by pointer ב C++ קיימת גם העברה by reference דוגמא: void foo(int &i) { i++; } int x = 7; foo(x); foo(5); //error
18
Reference כמו במקרה של העברה by pointer משתמשים בהעברה by reference כאשר רוצים שהפונקציה תשנה משתנים מחוצה לה אובייקטים גדולים - לא רוצים להעביר by value ולהעתיק void swap(int& p, int& q) { int t = p ; p = q; q = t ; } swap(x,y); // OK swap(x,5) ; // Error C++ void swap(int* p, int* q) { int t = *p ; assert(p != NULL); assert(q != NULL); *p = *q; *q = t ; } swap(&x,&y); C
19
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C Const Reference ניתן להגדיר const reference עם משמעות דומה ל-const pointer: int i = 8; const int &j = i; j = 7; // error - assignment to const reference int k = j; // OK, reading the value of const reference ניתן אפילו להגדיר const reference למספרים קבועים: const int &m = 17; const reference to literals - is used when passing parameters to functions: voidfoo(const int&); foo(7); // OK void bar(const int *); foo(&7); // error - you cannot apply & on a literal
20
טעות נפוצה: החזרת reference למשתנה לוקלי
int& foo() { int i; ... return i; } ה-reference יתייחס למשתנה שיפסיק להתקיים עם היציאה מהפונקציה
21
דוגמא נוספת לשימוש ב-reference
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C דוגמא נוספת לשימוש ב-reference int& arrayBounds (int* array, int size, int& max, int& min) { int i,sum = 0 ; max = min = array[0]; for (i=0 ; i<size; i++) { if (max < array[i]) max = array[i]; if (min > array[i]) min = array[i] ; sum += array[i] ; } return array[size/2]; מוחזר reference לאיבר האמצעי במערך מה היה קורה אם היינו מחזירים sum ?
22
הבדלים בין reference למצביע
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C הבדלים בין reference למצביע אתחול reference חייב להיות מאותחל מצביע לא חייב להיות מאותחל ערך NULL מצביע יכול להכיל ערך NULL אין ל-reference ערך NULL. הוא חייב להתייחס למשתנה כלשהו שינוי הצבעה/התייחסות מצביע יכול לשנות משתנה שהוא מצביע אליו reference יכול להתייחס למשתנה אחד בלבד בד''כ מעבירים by reference כאשר הערך לא יכול להיות NULL בעבודה עם אופרטורים (כדי שהאופרטורים יעבדו בצורה טבעית)
23
Reference אז בשביל מה צריך עוד שיטה להעברת פרמטרים ?
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C Reference אז בשביל מה צריך עוד שיטה להעברת פרמטרים ? וגם החזרת ערכי חזרה ? מה רע בהעברת פרמטרים by pointer ? התשובה בתרגול הבא (operator overloading) Stroustrup: “References are useful for several things, but the direct reason I introduced them in C++ was to support operator overloading”
24
Function Overloading: הבעיה
int max(int x, int y); נניח שבנוסף אנחנו רוצים פונקציה שמחזירה מקסימום של שלושה שלמים. איזה שם ניתן לה ? השם max כבר תפוס על ידי פונקציה קודמת int max3(int x, int y, int z); ואם נרצה פונקציה שמחזירה מקסימום של שני floats ? float max_float(float x, float y);
25
Function Overloading: הפתרון ב C++
אותו שם לכל פונקציות המקסימום ! int max(int x, int y); int max(int x, int y, int z); float max(float x, float y); חוסכים מאמץ מחשבתי בהמצאת שמות חדשים השמות יוצאים קצרים וטבעיים יותר משפר קריאות
26
Function Overloading: איך זה עובד ?
הפונקציה מזוהה על ידי השם שלה וגם על ידי הפרמטרים שהיא מקבלת כשהקומפיילר מזהה קריאה לפונקציה כלשהיא הוא מחפש כל הפונקציות עם אותו שם - המועמדות להיקרא מבין כל המועמדות, מחפש פונקציה שמתאימה ביותר לפי הפרמטרים אם יש "מנצחת יחידה" היא זאת שנקראת אם אין "מנצחת יחידה" יש שגיאת קומפילציה דו-משמעות (ambiguity)
27
:Function Overloadingכללים להתאמה
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C :Function Overloadingכללים להתאמה exact match ( + trivial conversions: int ->int&, int to const int) match with promotions (char,short int -> int, float->double) match with conversions (int->float, float -> int) match with user defined type conversions match with ellipsis (…) ellipsis מחוץ לחומר של הקורס נלמד user defined type conversions בהמשך
28
:Function Overloadingכללים להתאמה
הפונקציות לא נבדלות על ידי ערך מוחזר אסור שיהיו שתי פונקציות עם אותו שם ואותם פרמטרים שגיאת קומפילציה int foo(int i, float f) { ... } float foo(int i, float f) { שגיאת קומפילציה : foo עם הפרמטרים int ו-float כבר מוגדרת
29
:Function Overloadingדוגמא
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C :Function Overloadingדוגמא void print(int); void print(const char *); void print(double); void print(long); char c; int i ; short s; float f; print(c); print(i); print(s) ; print(f) ; print(‘a’); print(45); print(0.0) ; print (“a”); print(int) is called print(double) is called print(const char*) is called
30
:Function Overloadingדוגמא לדו משמעות
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C :Function Overloadingדוגמא לדו משמעות void foo(int i, float f); void foo(float f, int i); int x,y; foo(x,y); // error - no best matching function foo(x,(float)y); // OK. the first foo is called foo((float)x,y); // OK.the second foo is called
31
Default Parameters: המוטיבציה
קיימות פונקציות שברוב המקרים מקבלים ערך קבוע עבור פרמטר כלשהו ורק במקרים נדירים ערך אחר לדוגמא: הדפסת מספרים בבסיסים שונים. לרוב הבסיס יהיה 10
32
Default Parameters: המוטיבציה
void print_number(int n, int base ) ; void foo() { ... print_number (24,8); print_number (12,10); print_number (x,10); print_number (y,10); print_number (16,2); print_number (24,10); print_number (x+2,10); print_number (y-7,10); print_number (30,16); print_number (30,10); } ברוב המקרים הפרמטר השני הוא 10. האם אפשר לחסוך את כתיבתו ?
33
Default Parameters: המוטיבציה
הפתרון האפשרי - שימוש ב-Function Overloading מגדירים שתי פונקציות: void print_number(int n, int base ); // print number in base 10 void print_number(int n) ; void print_number(int n) { print_number(n,10); } שתי הפונקציות עושות כמעט אותו דבר. איך נמנע שיכפול קוד ?
34
Default Parameters: המוטיבציה
void print_number(int n, int base ) ; void print_number(int n) { print_number(n,10); } void foo() { ... print_number (24,8); print_number (12); print_number (x); print_number (y); print_number (16,2); print_number (24); print_number (x+2); print_number (y-7); print_number (30,16); print_number (30); } void print_number(int n, int base ) ; void foo() { ... print_number (24,8); print_number (12,10); print_number (x,10); print_number (y,10); print_number (16,2); print_number (24, 10); print_number (x+2,10); print_number (y-7,10); print_number (30,16); print_number (24,10); print_number (30,10); }
35
Default Parameters האם אפשר לחסוך את כתיבת שתי הפונקציות print_number ? הפתרון בC++ - פונקציה אחת עם פרמטר ברירה מחדל (default parameter) void print_number(int n, int base = 10); כשקוראים לפונקציה עם שני פרמטרים, הקריאה מתבצעת כרגיל כשקוראים לפונקציה עם פרמטר אחד, אוטומטית מוצב ערך ברירה מחדל במקום הפרמטר השני הפונקציה מתבצעת כרגיל, כאילו שקראו לה עם שני פרמטרים
36
Default Parameters שתי הקריאות זהות מבחינת הפונקציה:
print_number(17,10); print_number(17); אין שינוי במימוש של הפונקציה - רק בהצהרתה את ברירות מחדל של פרמטרים מציינים פעם אחת בלבד בד''כ בהצהרה על הפונקציה ב-header file void print_number(int n, int base = 10); void print_number(int n, int base) { ... }
37
Default Parameters ניתן לתת ערכי ברירת מחדל רק לפרמטרים האחרונים:
int foo(int a = 0, int b = 0, int c = 0); // OK int bar(int a , int b = 0, int c = 0); // OK int baz(int a = 0, int b = 0, int c); // Error int f(int a = 0, int b, int c = 0); // Error
38
Default Parameters void print_number(int n, int base ) ;
void print_number(int n) { print_number(n,10); } void foo() { ... print_number (24,8); print_number (12); print_number (x); print_number (y); print_number (16,2); print_number (24); print_number (x+2); print_number (y-7); print_number (30,16); print_number (30); } void print_number(int n, int base = 10) ; void foo() { ... print_number (24,8); print_number (12); print_number (x); print_number (y); print_number (16,2); print_number (24); print_number (x+2); print_number (y-7); print_number (30,16); print_number (30); }
39
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C מימוש ADTsב++C
40
מבנה הנתונים מחסנית תזכורת
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C מבנה הנתונים מחסנית תזכורת מחסנית הינה מבנה נתונים התומך בפעולות הבאות: push - הוסף איבר למחסנית. pop - הוצא את האיבר ה”צעיר” ביותר במחסנית. top - החזר את האיבר ה”צעיר” ביותר במחסנית. ובנוסף: create - צור מבנה נתונים מסוג מחסנית (ריק). destroy - הרוס את מבנה הנתונים מחסנית. print - הדפס את תוכן מבנה הנתונים. דוגמא למימוש אפשרי: מערך + מצביע למקום הפנוי הבא 1 2 8 topIndex
41
ADTsב ++C לעומת C ב-C מימשנו ADT בצורה הבאה:
קובץ header הכיל את הממשק הוגדר בו הטיפוס של ה-ADT הוצהרו בו פונקציות המטפלות ב- ADT בקובץ המימוש ((source הופיע המימוש של הפונקציות ב++C הוטמעה צורת עבודה זו בשפה ניתן להגדיר טיפוסים (מחלקות - classes) אשר כוללים בתוכם שדות של כל משתנה מאותו טיפוס (אובייקט של אותה מחלקה) פונקציות (מתודות) המופעלות עליו בשפות מונחות עצמים נהוג לקרוא לפונקציות ששייכות למחלקות - "מתודות" שמות נוספים: שיטה, method, member function
42
מימוש ב++Header File :C
enum Result {Success, Fail} ; struct Stack { int* array; int size, topIndex ; } ; שדות data members Result init (int initSize) ; void destroy() ; Result push (int element); Result pop (); Result top(int& element); Result print() ; פונקציות של המחלקה method or member functions
43
מימוש ב++C:Source File
מסמן שזוהי method של class Stack שם ה-method Result Stack::init (int initSize) { array = new int[initSize]; size = initSize; topIndex = 0 ; return Success ; } ערך מוחזר גישה ישירה לשדות של ה- struct כאילו הם משתנים לוקליים
44
מימוש ב++C:Source File
void Stack::destroy () { delete[] array; } Result Stack::push(int element) { if (topIndex == size){ return Fail ; array[topIndex] = element; topIndex++; return Success;
45
מימוש ב++C:Source File
Result Stack::pop () { if (topIndex == 0){ return Fail; } topIndex--; return Success; Result Stack::top (int& element) { if (topIndex < 1){ element = array[topIndex-1]; return Success;
46
שימוש ב-Stack ב++C לעומתC
#include “Stack.h” int main() { Stack stack; int i; stack.init (100) ; stack.push (1); stack.push(213); stack.pop (); stack.top(i); stack.destroy(); return 0; } C++ #include “Stack.h” int main() { Stack stack; int i; init (&stack,100) ; push (stack,1); push(stack,213); pop (stack); top(stack,&i); destroy(stack); return 0; } C קריאה לפונקציה מתבצעת בדיוק כמו גישה לשדה
47
המצביע this שימו לב לאופן השונה בו נקראו הפונקציות ב- C וב++C
stack.top(i);
48
המתודה, לאחר שהקומפיילר הוסיף את המצביע this
טיפוסו הנו מצביע למחלקה בה מוגדרת המתודה מכיל מצביע לאובייקט עליו נקראה המתודה בכל גישה לשדה של האובייקט או למתודה שלו מתוך מתודה אחרת הקומפיילר מוסיף הצבעה דרך this void Stack::top(int& e) { e = array[topIndex-1]; } void Stack::top(int& e) { e = this->array[this->topIndex-1]; } המתודה, לאחר שהקומפיילר הוסיף את המצביע this
49
הבעיה: הסתרת מידע מה הבעיה עם המחלקה הזאת ?
רמז: היא מופיעה בקובץ .h רמז: איזה עיקרון חשוב של ADT הופר פה ? struct Stack { int* array; int size, topIndex ; Result init (int size) ; void destroy() ; Result push (int e); Result pop (); Result top(int& e); Result print(); } ; אין הסתרת מידע ! כל השדות חשופים ! המשתמש יכול לגשת לשדות של המחלקה ישירות ולעקוף את הפונקציות
50
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C הפתרון: בקרת גישה בC++ להבדיל מ-ADTs ב-C לא מסתירים את מבנה המחלקה (השדות שלה) במקום זה מגבילים גישה לשדות שלה וגם לפונקציות "הפנימיות" של ADT משתמשים ב-access modifiers (מאפייני גישה) private (פרטי) רק הפונקציות של המחלקה יכולות לגשת לאיברים שהם private public (ציבורי) כל פונקציה יכולה לגשת לאיברים שהם public protected נלמד בפרק על הירושה
51
מחלקה Stack עם access modifiers
struct Stack { private: int* array; int size, topIndex ; public: Result init (int size) ; void destroy() ; Result push (int e); Result pop (); Result top(int& e); Result print(); } ; struct Stack { int* array; int size, topIndex ; Result init (int size) ; void destroy() ; Result push (int e); Result pop (); Result top(int& e); Result print(); } ;
52
הפתרון: בקרת גישה המשתמש של המחלקה יכול לראות את ההצהרות של כל השדות
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C הפתרון: בקרת גישה המשתמש של המחלקה יכול לראות את ההצהרות של כל השדות וגם את הצהרות של כל הפונקציות הפנימיות של המחלקה המשתמש לא יכול לגשת לאיברים שהם private לקרוא ערכים של השדות לכתוב לתוך השדות להפעיל פונקציות פנימיות (private) protected נלמד בפרק על הירושה
53
שמוש ב-class במקום struct
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C שמוש ב-class במקום struct מקובל להגדיר מחלקות על ידי המילה השמורה class ולא struct ההבדל בין struct ל- class: ב-class, אם לא צוין אחרת, אופן הגישה הנו private ב-struct, אם לא צוין אחרת, אופן הגישה הנו public ב-struct נהוג להשתמש לייצוג מחלקות ללא מתודות
54
שמוש ב-class במקום struct
class Stack { int* array; int size, topIndex ; public: Result init (int size) ; void destroy() ; Result push (int e); Result pop(); Result top(int& e); Result print(); } ; struct Stack { private: int* array; int size, topIndex ; public: Result init (int size) ; void destroy() ; Result push (int e); Result pop(); Result top(int& e); Result print(); } ;
55
בקרת גישה: Friend functions
class Stack { friend int second (const Stack &); int* array; int size, topIndex; public: … }; int second(const Stack& stack) { assert(stack.topIndex > 1); return stack.array[stack.topIndex-2]; } ניתן לתת גישה לחלק הפרטי של המחלקה בצורה סלקטיבית מגנון friends ניתן להכריז על פונקציה כלשהיא כחברה (friend) של המחלקה אז היא יכולה לגשת לשדות הפרטיים של המחלקה
56
Friend classes and Methods
class A { …. int foo(); … }; class B { … class C { friend int A::foo(); friend class B; ניתן להכריז על מחלקה שלמה כחברה של מחלקה כלשהיא אז כל המתודות שלה הן חברות של המחלקה method foo() of class A is a friend of class C all methods of class B are friends of class C
57
Friends בC++: נקודות נוספות
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C Friends בC++: נקודות נוספות מהעובדה שמחלקה A חברה של מחלקה B לא נגרר שמחלקה B היא חברה של מחלקה A מהעובדה שמחלקה A חברה של מחלקה B ומחלקה B חברה של מחלקה C לא נגרר שמחלקה A היא חברה של המחלקה C היחס של friends הוא לא סימטרי ולא טרנזיטיבי
58
מודולאריות, הסתרת מידע ו-friends
מחלקה טובה צריכה להסתיר כמה שיותר כמה שיותר "פאראנוידית" לגבי השדות שלה כל מה שלא נחוץ לשימוש שלה על ידי משתמשים חיצוניים צריך להיות private כמה שיותר בררנית לגבי "החברים" שלה כמה שיותר "סוציומאטית"- כמה שפחות friends מוסיפים friend רק אם האלטרנטיבות האחרות פחות טובות כל הוספה ללא צורך של friendפוגעת במודולאריות והסתרת מידע
59
const member functions: מוטיבציה
נניח שיש לנו פונקציה שמקבלת כפרמטר const Stack& : void foo(const Stack& stack) { ... stack.top(i); stack.push(j); stack.print(); } מה הבעיה בפונקציה הזאת ? הפונקציה מקבלת פרמטר const, אך משנה אותו על ידי פונקציה push
60
const member functions
כדי למנוע מקרים כמו בשקף הקודם, הפתרון בC++: על אובייקט שהוא const מותר לקרוא רק למתודות שהן const מתודה const היא מתודה שלא משנה את האובייקט עליו היא נקראת לא משנה את שדות האובייקט קוראת רק לפונקציות שהן const בעצמן ניתן להכריז על המתודה כ-const על ידי הוספת מילה שמורה const בסוף החתימה שלה הקומפיילר יוודא שהמתודה אכן לא משנה את האובייקט
61
const member functions
class Stack { int* array; int size, topIndex ; public: Result init (int size) ; void destroy() ; Result push (int element); Result pop(); Result top(int& element) const; Result print() const; } ; class Stack { int* array; int size, topIndex ; public: Result init (int size) ; void destroy() ; Result push (int element); Result pop(); Result top(int& element); Result print(); } ;
62
const member functions
void foo(const Stack& stack) { ... stack.top(i); // OK, top is a const function stack.push(j); // Error ! Calling a non-const // function on a const object stack.print(); // OK, print is a const function }
63
const member functions
class C { … int foo(); // will be called for non const objects int foo() const; //will be called for const // objects };
64
Const Correctness ככל שתשתמשו ב-const יותר - התוכנה תהיה בטיחותית יותר
כל פרמטר של פונקציה by pointer או by reference שהפונקציה לא אמורה לשנות - צריך להיות const כל פונקציה, שלא משנה את האובייקט עליו היא נקראת, צריכה להיות const יתרונות: תיעוד עצמי וידוא אוטומטי על ידי הקומפיילר
65
הבעיה: התנגשות של שמות הבעיה: נניח שאנחנו כותבים תוכנה בנקאית
התוכנה מורכבת מכמה חבילות - packages)חלקי קוד גדולים) Clients Investments GUI DB בכל אחת מהחבילות קיימת מחלקה Account ופונקציה reportError איך נמנע התנגשות של שמות ? איך נבדיל בין Account של Client וה-Account של GUI ?
66
הפתרון של C נוסיף קידומת לכל שם בכל חבילה: class clientsAccount { ...
}; void clientsPrintError(char*); class investmentsAccount { ... }; void investmentsPrintError(char*); class dbAccount { ... }; void dbPrintError(char*); class guiAccount { ... }; void guiPrintError(char*);
67
הפתרון של C: החסרונות צריך להוסיף קידומת לכל שם
שמות ארוכים ופחות טבעיים פגיעה בקריאות עבודה שחורה קשה לשנות שם של חבילה צריך לעבור על כל הקידומות ולשנות אותן
68
הפתרון של C++: namespaces
namespace Clients { class Account { ... }; void printError(char*); } namespace Investements{ class Account { ... }; void printError(char*); } namespace DB { class Account { ... }; void printError(char*); } namespace GUI{ class Account { ... }; void printError(char*); }
69
שימוש בשמות של עם namespace
void foo() { Clients::Account account; ... Clients::printError(...); GUI::printError(...); }
70
namespaces: הוראה using
ניתן לחסוך ציון namespace על ידי שימוש בהוראה using : using namespace Clients; void foo() { Account account; ... printError(...); GUI::printError(...); }
71
namespaces: הוראה using
using Clients::Account; using Clients::printError; void foo() { Account account; ... printError(...); GUI::printError(...); }
72
קלט/פלט ב++C ב ++ C קלט ופלט מתבצע ע”י “הזרמת” נתונים באמצעות האופרטורים >> ו-<<. משתמשים בערוצים (streams) הערוצים הסטנדרטיים הם cin - ערוץ הקלט הסטנדרטי, כמו stdin ב-C cout - ערוץ הפלט הסטנדרטי, כמו stdout ב-C cerr - ערוץ השגיאות הסטנדרטי, כמו stderr ב-C מוגדרים בקובץ header סטנדרטי iostream ב-C++ קבצי ה-header הסטנדרטיים הם ללא סיומת .h נמצאים ב-namespace std
73
קלט/פלט ב++C C C++ #include <iostream> using std::cin;
מבוא לתכנות מערכות תרגול 10: הכרות עם ++C קלט/פלט ב++C #include <iostream> using std::cin; using std::cout; using std::cerr; using std::endl; int i = 17, j; double d; cout << "A string " << i; cin >> j >> d; cerr << “Error!” << endl; #include <stdio.h> int i = 17, j; double d; fprintf (stdout,“%s %d”, “A string”, i); fscanf (stdin, “%d %lf”, &j, &d); fprintf(stderr, “Error !\n”); C C++
74
קלט/פלט ב++C: היתרונות
הקומפיילר מזהה את הטיפוסים של המשתנים בעצמו חוסך התעסקות עם %d, %lf, %s וכו' חוסך טעיות קוד קצר יותר וקריא יותר הקלט עובד על משתנים ולא על הכתובות שלהם חוסך טעויות ב-scanf/fscanf
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.