Presentation is loading. Please wait.

Presentation is loading. Please wait.

תרגול 8 היכרות עם ++C. C++ מעל שפת Cשפת תכנות שנבנתה מעל שפת C –כמעט כל תכנית ב -C היא גם תכנית ב C++ עד כדי מספר הבדלים קטן C99 שאימץ כמה מהתכונות C++

Similar presentations


Presentation on theme: "תרגול 8 היכרות עם ++C. C++ מעל שפת Cשפת תכנות שנבנתה מעל שפת C –כמעט כל תכנית ב -C היא גם תכנית ב C++ עד כדי מספר הבדלים קטן C99 שאימץ כמה מהתכונות C++"— Presentation transcript:

1 תרגול 8 היכרות עם ++C

2 C++ מעל שפת Cשפת תכנות שנבנתה מעל שפת C –כמעט כל תכנית ב -C היא גם תכנית ב C++ עד כדי מספר הבדלים קטן C99 שאימץ כמה מהתכונות C++ C99 - סטנדרט של C, שאימץ כמה מהתכונות של C++ g++הקומפיילר של C++ שאנחנו נשתמש בקורס הוא g++ לקבצי C++ נהוג לתת סיומות –.h לקובץ header, כמו ב -C –.cpp לקובץ מימוש (source file) 2

3 הערות ב C++ // ועד סוף השורה C99 –גם ב -C99 –ניתן להשתמש גם ב -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; } 3

4 הגדרת משתנים ב C++ לא רק בתחילת בלוקב C++ ניתן להגדיר משתנים לא רק בתחילת בלוק –גם ב -C99 int foo(int size){ int i; SetResult setResult; ListResult listResult;... for(i = 0; i < size; ++i)... setResult = setCreate(...); for(i = 0; i < size; ++i)... listResult = listCreate(...);... { int foo(int size){ int i; SetResult setResult; ListResult listResult;... for(i = 0; i < size; ++i)... setResult = setCreate(...); for(i = 0; i < size; ++i)... listResult = listCreate(...);... { int foo(int size){... for(int i = 0; i < size; ++i)... SetResult setResult = setCreate(...); for(int i = 0; i < size; ++i) ListResult listResult = listCreate(...);... { int foo(int size){... for(int i = 0; i < size; ++i)... SetResult setResult = setCreate(...); for(int i = 0; i < size; ++i) ListResult listResult = listCreate(...);... { עדיף להכריז על משתנים כמה שיותר קרוב לשימוש הראשון שלהם C before C99 C++/C99 4

5 זיכרון דינמי ב C++: הקצאת עצמים בודדים int *pi = new int ; *pi = 6;... delete pi; int *pi = new int ; *pi = 6;... delete pi; int *pi = (int*)malloc(sizeof(int)) ; if(pi == NULL){... /* out of memory */ } *pi = 6;... free(pi); int *pi = (int*)malloc(sizeof(int)) ; if(pi == NULL){... /* out of memory */ } *pi = 6;... free(pi); C++ new type new type; delete pointer delete pointer; C malloctype malloc(sizeof(type)); freepointer free(pointer); אין צורך ב -casting אין צורך בבדיקת תוצאה של new 5

6 זיכרון דינמי ב C++: הקצאת מערכים int elements_number =...; int *pi = new int[elements_number]; pi[3] = 6;... delete[] pi; int elements_number =...; int *pi = new int[elements_number]; pi[3] = 6;... delete[] pi; int elements_number =...; int *pi = (int*)malloc(sizeof(int) * elements_number) ; if(pi == NULL){... /* out of memory */ } pi[3] = 6;... free(pi); int elements_number =...; int *pi = (int*)malloc(sizeof(int) * elements_number) ; if(pi == NULL){... /* out of memory */ } pi[3] = 6;... free(pi); C++ new type[#elements] new type[#elements]; delete[] pointer delete[] pointer; C malloctype*#elements malloc(sizeof(type)*#elements); freepointer free(pointer); 6

7 זיכרון דינמי ב C++ לא נהוג free/mallocב C++ מותר אך לא נהוג להשתמש בפונקציות free/malloc אסור לערבב –בכל מקרה, אסור לערבב בין new/delete ו - free/malloc free newאסור להפעיל free על הזיכרון שהוקצה על ידי new delete mallocאסור להפעיל delete על הזיכרון שהוקצה על ידי malloc 7

8 זיכרון דינמי ב C++ חייבים להתאים newdeleteחייבים להתאים בין סוגי new ו -delete – delete new – delete אחרי new – delete[]new[] – delete[] אחרי new[] מותר להפעיל NULLמותר להפעיל delete על NULL if(ptr != NULL) { delete ptr; } 8

9 const ב C++ לשם טיפוסקבועתוספת לשם טיפוס, מציינת טיפוס של קבוע const type const –משתנים מסוג const חייבים לאתחל לא ניתן לבצע השמה אליהם C99קיימים גם ב -C99 #define MAX_SIZE 120 const int maxSize = 120; C before C99 C++/C99 9

10 const ב C++ i = 7; // OK. initialization to 7 const int i = 7; // OK. initialization to 7 i = 17; // error: assignment to a const variable j; // error: definition of a const variable const int j; // error: definition of a const variable // without initialization // without initialization // OK. assignment of a const variable to int k = i; // OK. assignment of a const variable to // a non const variable // a non const variable const int intArray[] = { 7, 17, 27, 777 }; // error: assignment to a const intArray[2] = 8; // error: assignment to a const שימו לב להבדל בין אתחול להשמה 10

11 מצביעים ל -const ב C++ מצביעיםניתן להגדיר מצביעים לטיפוסים קבועים const type* למשל, const int* pi; מצביע pi הוא משתנה מסוג מצביע ל - const int –יכול להחזיק כתובות של משתנים מסוג const int typeconst type*ניתן לבצע השמה של כתובת של משתנה מטיפוס type לתוך מצביע מטיפוס const type* ניתן להמיר –בפרט, ניתן להמיר int* ל const int*- –אך לא להפך 11

12 מצביעים ל -const ב C++ באילו ביטויים יש שגיאה ? const int i = 7; const int* cpi = &i; *cpi = 17; int j = 17; int* pi = &j; cpi = &j; cpi= pi; pi = cpi; 12

13 שימוש ב -const בפונקציות : פרמטרים פרמטרים לפונקציהניתן להגדיר כ - const פרמטרים לפונקציה –חשוב מאוד מצביעים –חשוב מאוד עבור פרמטרים שהם מצביעים /* 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); /* 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); int foo(int* i, const char* c, float*f, const double* d); int foo(int* i, const char* c, float*f, const double* d); C before C99 C++/C99 חוסך תיעוד הקומפיילר מוודא שלא משנים פרמטרים שהם const 13

14 שימוש ב -const בפונקציות : ערכי חזרה ערכי חזרה של פונקציהניתן להגדיר כ - const ערכי חזרה של פונקציה –חשוב מאוד מצביעים –חשוב מאוד עבור ערכי חזרה שהם מצביעים /* please do not change the returned string !!! */ char* foo(int i); /* please do not change the returned string !!! */ char* foo(int i); const char* foo(int i); C before C99 C++/C99 חוסך תיעוד הקומפיילר מוודא שלא משנים ערכי חזרה שהם const 14

15 משתנים מיוחסים - Reference type& = ; שם נוסף reference הוא שם נוסף למשתנה קיים לדוגמה : &int referenceint הביטוי &int משמעותו reference לטיפוס int לא לבלבל " הכתובת של משתנה "x – לא לבלבל עם &x שמשמעותו " הכתובת של משתנה "x בפועל מתבצעתכל פעולה שמפעילים על reference, בפועל מתבצעת על המשתנה שה -reference מתייחס אליו int i = 7; int &j = i; 15

16 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 16

17 Reference העברתפרמטרים ערכי חזרההעברת פרמטרים לפונקציות / החזרת ערכי חזרה ב -C העברת פרמטרים מתבצעת – by value – by pointer by referenceב C++ קיימת גם העברה by reference דוגמא : void foo(int &i) { i++; } int x = 7; foo(x); //error foo(5); //error 17

18 Reference כמו במקרה של העברה by pointer משתמשים בהעברה by reference כאשר תשנה משתנים מחוצה לה –רוצים שהפונקציה תשנה משתנים מחוצה לה –אובייקטים גדולים –אובייקטים גדולים - לא רוצים להעביר by value ולהעתיק void swap(int* p, int* q) { int t = *p ; assert(p != NULL); assert(q != NULL); *p = *q; *q = t ; } swap(&x,&y); void swap(int* p, int* q) { int t = *p ; assert(p != NULL); assert(q != NULL); *p = *q; *q = t ; } swap(&x,&y); void swap(int& p, int& q) { int t = p ; p = q; q = t ; } swap(x,y); // OK swap(x,5) ; // Error void swap(int& p, int& q) { int t = p ; p = q; q = t ; } swap(x,y); // OK swap(x,5) ; // Error CC++ 18

19 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; 19

20 טעות נפוצה : החזרת reference למשתנה לוקלי אסור להחזיר reference למשתנה לוקליכמו במקרה של מצביעים : אסור להחזיר reference למשתנה לוקלי : int& foo() { int i;... return i; } יפסיק להתקייםה -reference יתייחס למשתנה שיפסיק להתקיים עם היציאה מהפונקציה 20

21 דוגמא נוספת לשימוש ב -reference int& arrayBounds (int* array, int size, int& max, int& min) { int sum = 0 ; max = min = array[0]; for (int 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 ? 21

22 הבדלים בין reference למצביע אתחולאתחול – reference חייב להיות מאותחל –מצביע לא חייב להיות מאותחל ערך NULLערך NULL –מצביע יכול להכיל ערך NULL –אין ל -reference ערך NULL. הוא חייב להתייחס למשתנה כלשהו שינוי הצבעה / התייחסותשינוי הצבעה / התייחסות –מצביע יכול לשנות את ערכו להצביע לכתובת חדשה – reference יכול להתייחס למשתנה אחד בלבד 22

23 Reference אז בשביל עוד שיטהאז בשביל מה צריך עוד שיטה להעברת פרמטרים ? –וגם החזרת ערכי חזרה ? מה רע בהעברת פרמטרים by pointer ? –מניעת שגיאות –תחביר יותר נוח בלי * ו -& מעצבנים –הסיבה העיקרית בתרגול הבא (operator overloading) 23

24 Function Overloading: הבעיה פונקציה מקסימום של שני שלמיםנניח, אנחנו רוצים לכתוב ב -C פונקציה שמחזירה מקסימום של שני שלמים : int max(int x, int y); פונקציה מקסימום של שלושה שלמיםנניח שבנוסף אנחנו רוצים פונקציה שמחזירה מקסימום של שלושה שלמים. – איזה שם ניתן לה ? כבר תפוס –השם max כבר תפוס על ידי פונקציה קודמת int max3(int x, int y, int z); פונקציה מקסימום של שני floatsואם נרצה פונקציה שמחזירה מקסימום של שני floats ? float max_float(float x, float y); 24

25 Function Overloading: הפתרון ב C++ אותו שם לכל פונקציותאותו שם לכל פונקציות המקסימום ! max int max(int x, int y); max int max(int x, int y, int z); max float max(float x, float y); מאמץ מחשבתיחוסכים מאמץ מחשבתי בהמצאת שמות חדשים קצרים וטבעייםהשמות יוצאים קצרים וטבעיים יותר קריאות –משפר קריאות 25

26 Function Overloading: איך זה עובד ? השםוגם הפרמטריםהפונקציה מזוהה על ידי השם שלה וגם על ידי הפרמטרים שהיא מקבלת קריאה לפונקציהכשהקומפיילר מזהה קריאה לפונקציה כלשהיא הוא : המועמדות –מחפש את כל הפונקציות עם אותו שם - המועמדות המועמדותשמתאימה ביותרלפי הפרמטרים –מבין כל המועמדות, מחפש פונקציה שמתאימה ביותר לפי הפרמטרים " מנצחת יחידה "אם יש " מנצחת יחידה " היא זאת שנקראת " מנצחת יחידה " שגיאת קומפילציהאם אין " מנצחת יחידה " יש שגיאת קומפילציה –דו - משמעות (ambiguity) 26

27 :Function Overloading כללים להתאמה 1.exact match trivial conversions 1.exact match ( + trivial conversions: int ->int&, int to const int) promotions 2.match with promotions (char,short int -> int, float->double) conversions 3.match with conversions (int->float, float -> int) user defined type conversions 4.match with user defined type conversions ellipsis 5.match with ellipsis (…) נלמד user defined type conversions בהמשך ellipsis מחוץ לחומר של הקורס 27

28 :Function Overloading כללים להתאמה לא נבדלות ערך מוחזרהפונקציות לא נבדלות על ידי ערך מוחזר אסור עם אותו שם ואותם פרמטריםאסור שיהיו שתי פונקציות עם אותו שם ואותם פרמטרים –שגיאת קומפילציה foo(int i, float f) int foo(int i, float f) {... } foo(int i, float f) float foo(int i, float f) {... } שגיאת קומפילציה : foo עם הפרמטרים int ו - float כבר מוגדרת 28

29 :Function Overloading דוגמא 1.void print(int); 2.void print(const char *); 3.void print(double); 4.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”); 29

30 :Function Overloading דוגמא לדו משמעות 1.void foo(int i, float f); 2.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 30

31 Default Parameters: המוטיבציה שברוב המקריםערך קבועפרמטרקיימות פונקציות שברוב המקרים מקבלים ערך קבוע עבור פרמטר כלשהו במקרים נדירים ערך אחר –ורק במקרים נדירים ערך אחר לרובלדוגמא : הדפסת מספרים בבסיסים שונים. לרוב הבסיס יהיה 10 31

32 Default Parameters: המוטיבציה 32 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 (12,10); print_number (x+2,10); print_number (y-7,10); print_number (30,16); print_number (24,10); print_number (12,10); print_number (x+2,10); print_number (y-7,10); print_number (30,10); } ברוב המקרים הפרמטר השני הוא 10. האם אפשר לחסוך את כתיבתו ?

33 Default Parameters: המוטיבציה Function Overloadingהפתרון האפשרי - שימוש ב -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); } 33 שתי הפונקציות עושות כמעט אותו דבר. איך נמנע שיכפול קוד ?

34 Default Parameters: המוטיבציה 34 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 (12,10); print_number (x+2,10); print_number (y-7,10); print_number (30,16); print_number (24,10); print_number (12,10); print_number (x+2,10); print_number (y-7,10); print_number (30,10); } 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 (12); print_number (x+2); print_number (y-7); print_number (30,16); print_number (24); print_number (12); print_number (x+2); print_number (y-7); print_number (30); }

35 Default Parameters לחסוךכתיבת שתי הפונקציותהאם אפשר לחסוך את כתיבת שתי הפונקציות print_number ? פרמטר ברירה מחדלהפתרון ב C++ - פונקציה אחת עם פרמטר ברירה מחדל (default parameter) = 10 void print_number(int n, int base = 10); שני פרמטרים כרגילכשקוראים לפונקציה עם שני פרמטרים, הקריאה מתבצעת כרגיל פרמטר אחדאוטומטית ערך ברירה מחדלכשקוראים לפונקציה עם פרמטר אחד, אוטומטית מוצב ערך ברירה מחדל במקום הפרמטר השני כרגיל –הפונקציה מתבצעת כרגיל, כאילו שקראו לה עם שני פרמטרים 35

36 Default Parameters זהותשתי הקריאות זהות מבחינת הפונקציה : 1.print_number(17,10); 2.print_number(17); אין שינוי מימושהצהרתהאין שינוי במימוש של הפונקציה - רק בהצהרתה פעם אחת בלבדאת ברירות מחדל של פרמטרים מציינים פעם אחת בלבד –בד '' כ בהצהרה על הפונקציה ב -header file void print_number(int n, int base = 10); void print_number(int n, int base) {... } 36

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 37

38 Default Parameters 38 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 (12); print_number (x+2); print_number (y-7); print_number (30,16); print_number (24); print_number (12); print_number (x+2); print_number (y-7); 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 (12); print_number (x+2); print_number (y-7); print_number (30,16); print_number (24); print_number (12); print_number (x+2); print_number (y-7); print_number (30); }

39 הבעיה : התנגשות של שמות תוכנה בנקאיתהבעיה : נניח שאנחנו כותבים תוכנה בנקאית התוכנה מורכבת מכמה חבילות - packages) חלקי קוד גדולים ) 1.Clients 2.Investments 3.GUI 4.Database בכל אחת מהחבילותבכל אחת מהחבילות קיימת מחלקה Account ופונקציה reportError התנגשות של שמותאיך נמנע התנגשות של שמות ? איך נבדיל איך נבדיל בין Account של Client וה -Account של GUI ? 39

40 הפתרון של C נוסיף קידומתנוסיף קידומת לכל שם בכל חבילה : 40 class clientsAccount {... }; void clientsPrintError(char*); class clientsAccount {... }; void clientsPrintError(char*); class investmentsAccount {... }; void investmentsPrintError(char*); class investmentsAccount {... }; void investmentsPrintError(char*); class dbAccount {... }; void dbPrintError(char*); class dbAccount {... }; void dbPrintError(char*); class guiAccount {... }; void guiPrintError(char*); class guiAccount {... }; void guiPrintError(char*);

41 הפתרון של C: החסרונות להוסיף קידומת לכל שםצריך להוסיף קידומת לכל שם ארוכים ופחות טבעיים –שמות ארוכים ופחות טבעיים פגיעה בקריאותפגיעה בקריאות –עבודה שחורה –קשה לשנות –קשה לשנות שם של חבילה לעבור על כל הקידומותצריך לעבור על כל הקידומות ולשנות אותן 41

42 הפתרון של C++: namespaces 42 namespace Clients { class Account {... }; void printError(char*); } namespace Clients { class Account {... }; void printError(char*); } namespace Investements{ class Account {... }; void printError(char*); } namespace Investements{ class Account {... }; void printError(char*); } namespace DB { class Account {... }; void printError(char*); } namespace DB { class Account {... }; void printError(char*); } namespace GUI{ class Account {... }; void printError(char*); } namespace GUI{ class Account {... }; void printError(char*); }

43 שימוש בשמות של עם namespace void foo() { Clients::Account account;... Clients::printError(...);... GUI::printError(...); } 43

44 namespaces: הוראה using לחסוך usingניתן לחסוך ציון namespace על ידי שימוש בהוראה using: using namespace Clients; void foo() { Account account;... printError(...);... GUI::printError(...); } 44

45 namespaces: הוראה using יותר ספציפיים רק על מה שבאמת משתמשים בועדיף להיות יותר ספציפיים ולהפעיל using רק על מה שבאמת משתמשים בו –ולא להכליל כל השמות ללא צורך –ולא להכליל את כל השמות ב -namespace ללא צורך using Clients::Account; using Clients::printError; void foo() { Account account;... printError(...);... GUI::printError(...); } 45

46 קלט / פלט ב ++C “הזרמת” נתוניםב ++ C קלט ופלט מתבצע ע”י “הזרמת” נתונים באמצעות האופרטורים >> ו-<<. בערוצים streamsמשתמשים בערוצים (streams) הערוצים הסטנדרטיים הם – cin הקלט stdin – cin - ערוץ הקלט הסטנדרטי, כמו stdin ב -C – cout הפלט stdout – cout - ערוץ הפלט הסטנדרטי, כמו stdout ב -C – cerr השגיאות stderr – cerr - ערוץ השגיאות הסטנדרטי, כמו stderr ב -C iostream –מוגדרים בקובץ header סטנדרטי iostream ב -C++ קבצי ה -header הסטנדרטיים הם ללא סיומת.h namespace std –נמצאים ב -namespace std 46

47 קלט / פלט ב ++C 47 #include int i = 17, j; double d; fprintf (stdout,“%s %d”, “A string”, i); fscanf (stdin, “%d %lf”, &j, &d); fprintf(stderr, “Error !\n”); #include int i = 17, j; double d; fprintf (stdout,“%s %d”, “A string”, i); fscanf (stdin, “%d %lf”, &j, &d); fprintf(stderr, “Error !\n”); #include 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 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; C C++

48 קלט / פלט ב ++C: היתרונות הקומפיילר מזהה הטיפוסים בעצמוהקומפיילר מזהה את הטיפוסים של המשתנים בעצמו –חוסך התעסקות –חוסך התעסקות עם %d, %lf, %s וכו ' –חוסך טעויות –קוד קצר יותר וקריא יותר על משתנים ולא על הכתובותהקלט עובד על משתנים ולא על הכתובות שלהם –חוסך טעויות –חוסך טעויות ב -scanf/fscanf –קוד קצר יותר וקריא יותר הרחבהמאפשר הרחבה לטיפוסים נוספים –כיצד ? נראה בתרגולים הבאים 48

49 מימוש ADTs ב ++C

50 מבנה הנתונים מחסנית תזכורת מחסנית הינה מבנה נתונים התומך בפעולות הבאות : push - הוסף איבר למחסנית. pop - הוצא את האיבר ה ” צעיר ” ביותר במחסנית. top - החזר את האיבר ה ” צעיר ” ביותר במחסנית. ובנוסף : create - צור מבנה נתונים מסוג מחסנית ( ריק ). destroy - הרוס את מבנה הנתונים מחסנית. print - הדפס את תוכן מבנה הנתונים. topIndex דוגמא למימוש אפשרי: מערך + מצביע למקום הפנוי הבא 50 1 1 2 2 8 8

51 ADTs ב ++C לעומת C ב -C מימשנו ADT בצורה הבאה : הממשק –קובץ header הכיל את הממשק הטיפוסהוגדר בו הטיפוס של ה -ADT פונקציותהוצהרו בו פונקציות המטפלות ב - ADT המימוש –בקובץ המימוש ((source הופיע המימוש של הפונקציות הוטמעהב -++C הוטמעה צורת עבודה זו בשפה מחלקותניתן להגדיר טיפוסים ( מחלקות - classes) אשר כוללים בתוכם –שדות –שדות של כל משתנה מאותו טיפוס ( אובייקט של אותה מחלקה ) –פונקציות –פונקציות ( מתודות ) המופעלות עליו 51 בשפות מונחות עצמים נהוג לקרוא לפונקציות ששייכות למחלקות - " מתודות " שמות נוספים : שיטה, method, member function

52 מימוש ב ++Header File :C enum Result {Success, Fail} ; struct Stack { int* array; int size, topIndex ; } ; שדות data members פונקציות של המחלקה method or member functions Result init (int initSize) ; void destroy() ; Result push (int element); Result pop (); Result top(int& element); Result print() ; 52 אין צורך ב -typedef ב -++C

53 מימוש ב ++C:Source File Result Stack::init (int initSize) { array = new int[initSize]; size = initSize; topIndex = 0 ; return Success ; } ערך מוחזר מסמן שזוהי method של class Stack שם ה -method גישה ישירה לשדות של ה - struct כאילו הם משתנים לוקליים 53

54 מימוש ב ++C:Source File void Stack::destroy () { delete[] array; } Result Stack::push(int element) { if (topIndex == size){ return Fail ; } array[topIndex] = element; topIndex++; return Success; } 54

55 מימוש ב ++C:Source File Result Stack::pop () { if (topIndex == 0){ return Fail; } topIndex--; return Success; } Result Stack::top (int& element) { if (topIndex < 1){ return Fail; } element = array[topIndex-1]; return Success; } 55

56 שימוש ב -Stack ב ++C לעומת C 56 #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; } #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; } #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; } #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; } CC++ קריאה לפונקציה מתבצעת בדיוק כמו גישה לשדה

57 המצביע this נקראו הפונקציותשימו לב לאופן השונה בו נקראו הפונקציות ב - C וב ++C להעביר מצביע לאובייקט – ב -C נדרשנו להעביר מצביע לאובייקט ( מבנה ) עליו על הפונקציה לעבוד באופן שונה –ב ++C קראנו לפונקציה של המחלקה באופן שונה : stack.top(i); 57

58 המצביע this פרמטר סמוי thisכאשר נקראת מתודה כלשהי נשלח לה פרמטר סמוי בשם this מצביע למחלקה –טיפוסו הנו מצביע למחלקה בה מוגדרת המתודה מצביע לאובייקט –מכיל מצביע לאובייקט עליו נקראה המתודה thisבכל גישה לשדה של האובייקט או למתודה שלו מתוך מתודה אחרת הקומפיילר מוסיף הצבעה דרך this void Stack::top(int& e) { e = array[topIndex-1]; } void Stack::top(int& e) { e = array[topIndex-1]; } void Stack::top(int& e) { e = this->array[this->topIndex-1]; } void Stack::top(int& e) { e = this->array[this->topIndex-1]; } המתודה, לאחר שהקומפיילר הוסיף את המצביע this 58

59 הבעיה : הסתרת מידע 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(); } ; 59 מה הבעיה עם המחלקה הזאת ? רמז : היא מופיעה בקובץ.h רמז : איזה עיקרון חשוב של ADT הופר פה ? אין הסתרת מידע ! כל השדות חשופים ! המשתמש יכול לגשת לשדות של המחלקה ישירות ולעקוף את הפונקציות

60 הפתרון : בקרת גישה לא מסתירים את מבנהב C++ להבדיל מ -ADTs ב -C לא מסתירים את מבנה המחלקה ( השדות שלה ) מגבילים גישהבמקום זה מגבילים גישה לשדות שלה –וגם לפונקציות " הפנימיות " של ADT access modifiersמשתמשים ב -access modifiers ( מאפייני גישה ) – private – private ( פרטי ) רק הפונקציות של המחלקהרק הפונקציות של המחלקה יכולות לגשת לאיברים שהם private – public – public ( ציבורי ) כל פונקציהכל פונקציה יכולה לגשת לאיברים שהם public 60

61 מחלקה Stack עם access modifiers 61 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(); } ; 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(); } ;

62 הפתרון : בקרת גישה יכול לראותהמשתמש של המחלקה יכול לראות את ההצהרות של כל השדות –וגם את הצהרות של כל הפונקציות הפנימיות של המחלקה לא יכול לגשתהמשתמש לא יכול לגשת לאיברים שהם private –לקרוא ערכים –לקרוא ערכים של השדות –לכתוב –לכתוב לתוך השדות –להפעיל –להפעיל פונקציות פנימיות (private) 62

63 שמוש ב -class במקום struct מחלקות class structמקובל להגדיר מחלקות על ידי המילה השמורה class ולא struct struct classההבדל בין struct ל- class: classprivate –ב-class, אם לא צוין אחרת, אופן הגישה הנו private structpublic –ב-struct, אם לא צוין אחרת, אופן הגישה הנו public struct נהוג למחלקות ללא מתודותב-struct נהוג להשתמש לייצוג מחלקות ללא מתודות 63

64 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(); } ; שמוש ב -class במקום struct 64 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(); } ;

65 בקרת גישה : Friend functions class Stack { friend 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 –מגנון friends פונקציה friend המחלקהניתן להכריז על פונקציה כלשהיא כחברה (friend) של המחלקה לגשת לשדות הפרטיים המחלקה –אז היא יכולה לגשת לשדות הפרטיים של המחלקה 65

66 Friend classes and Methods class A { …. int foo(); … }; class B { … }; class C { friend int A::foo(); friend class B; }; מחלקה שלמהכחברהניתן להכריז על מחלקה שלמה כחברה של מחלקה כלשהיא כל המתודות שלה חברות –אז כל המתודות שלה הן חברות של המחלקה 66 method foo() of class A is a friend of class C all methods of class B are friends of class C

67 Friends ב C++: נקודות נוספות A חברה של B לא נגרר B היא חברה של Aמהעובדה שמחלקה A חברה של מחלקה B לא נגרר שמחלקה B היא חברה של מחלקה A A חברה של B B חברה של C לא נגרר A היא חברה של Cמהעובדה שמחלקה A חברה של מחלקה B ומחלקה B חברה של מחלקה C לא נגרר שמחלקה A היא חברה של המחלקה C 67

68 מודולאריות, הסתרת מידע ו -friends להסתירמחלקה טובה צריכה להסתיר כמה שיותר " פרנואידית " –כמה שיותר " פרנואידית " לגבי השדות שלה לא נחוץמשתמשים חיצוניים private –כל מה שלא נחוץ לשימוש שלה על ידי משתמשים חיצוניים צריך להיות private בררנית " החברים " –כמה שיותר בררנית לגבי " החברים " שלה " סוציומאטית "friends –כמה שיותר " סוציומאטית "- כמה שפחות friends friend רק אם האלטרנטיבות האחרות פחות טובותמוסיפים friend רק אם האלטרנטיבות האחרות פחות טובות ללא צורך friend פוגעתכל הוספה ללא צורך של friend פוגעת במודולאריות והסתרת מידע 68

69 const member functions: מוטיבציה פונקציה כפרמטרנניח שיש לנו פונקציה שמקבלת כפרמטר const Stack& const Stack& : void foo(const Stack& stack) {... stack.top(i);... stack.push(j); stack.print(); } 69 מה הבעיה בפונקציה הזאת ? הפונקציה מקבלת פרמטר const, אך משנה אותו על ידי פונקציה push

70 const member functions הפתרון ב C++ const רק למתודות constכדי למנוע מקרים כמו בשקף הקודם, הפתרון ב C++: על אובייקט שהוא const מותר לקרוא רק למתודות שהן const const שלא משנה את האובייקטמתודה const היא מתודה שלא משנה את האובייקט עליו היא נקראת –לא משנה –לא משנה את שדות האובייקט –קוראת רק לפונקציות שהן const בעצמן const constניתן להכריז על המתודה כ -const על ידי הוספת מילה שמורה const בסוף החתימה שלה –הקומפיילר יוודא לא משנה את האובייקט –הקומפיילר יוודא שהמתודה אכן לא משנה את האובייקט 70

71 const member functions 71 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(); } ; class Stack { int* array; int size, topIndex ; : public: Result init (int size) ; void destroy() ; Result push (int element); Result pop(); const Result top(int& element) const; const Result print() const; } ;

72 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 } 72

73 const member functions מתודה const חלק מחתימתההגדרת מתודה כ - const מהווה חלק מחתימתה שתי מתודות אותו שם ואותם פרמטרים constיתכנו שתי מתודות עם אותו שם ואותם פרמטרים, אך אחת const והשנייה לא class C { … int foo();// will be called for non const objects int foo() const; //will be called for const // objects }; 73

74 Const Correctness const בטיחותיתככל שתשתמשו ב -const יותר - התוכנה תהיה בטיחותית יותר משתנה שלא אמור להשתנות –כל משתנה שלא אמור להשתנות עדיף שיהיה const פרמטר לא אמורה לשנות –כל פרמטר של פונקציה by pointer או by reference שהפונקציה לא אמורה לשנות - צריך להיות const פונקציהשלא משנה את האובייקט –כל פונקציה, שלא משנה את האובייקט עליו היא נקראת, צריכה להיות const יתרונות : 1. תיעוד 1. תיעוד עצמי 2. וידוא אוטומטי 2. וידוא אוטומטי על ידי הקומפיילר 74

75 75 http://abstrusegoose.com/249


Download ppt "תרגול 8 היכרות עם ++C. C++ מעל שפת Cשפת תכנות שנבנתה מעל שפת C –כמעט כל תכנית ב -C היא גם תכנית ב C++ עד כדי מספר הבדלים קטן C99 שאימץ כמה מהתכונות C++"

Similar presentations


Ads by Google