01- from C to C++

Report
‫תכנות מכוון עצמים ו‪++C -‬‬
‫יחידה ‪01‬‬
‫מ‪ C -‬ל‪++C -‬‬
‫קרן כליף‬
:‫ביחידה זו נלמד‬
‫ הגדרת תכנות מכוון עצמים‬
:++C -‫ ל‬C ‫ השוני הסינטקטי בין‬
printf  cout 
scanf  cin 
gets  cin.getline 
malloc  new 
free  delete 
‫ טיפוס התייחסות‬
)namespace( ‫ מרחבי שמות‬
© Keren Kalif
2
‫מה אנחנו יודעים? תכנות פרוצדורלי‬
‫‪ ‬בקורס ‪ C‬למדנו לתכנת במתודלוגיה הנקראת תכנות פרוצדורלי‬
‫‪ ‬התכנות היה בסגנון ‪Top-Down‬‬
‫‪ ‬הדגש בתוכניות היה על פונקציות והמידע המועבר בינהן‬
‫‪ ‬דוגמא‪:‬‬
‫‪ ‬אם רצינו לטפל במטריצה‪ ,‬היינו רושמים את הפונקציות הבאות‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫פונקציה שיודעת להקצות מטריצה‬
‫פונקציה שיודעת לקלוט נתונים למטריצה‬
‫פונקציה שיודעת להדפיס את המטריצה‬
‫פונקציה שיודעת לשחרר את נתוני המטריצה‬
‫‪ ‬כל אחת מהפונקציות הנ"ל הייתה צריכה לקבל את המטריצה‬
‫ומימדיה כפרמטרים‬
‫‪3‬‬
‫‪© Keren Kalif‬‬
‫מה נלמד? תכנות מכוון עצמים‬
‫‪ ‬בקורס זה נלמד מתודולוגית תכנות הנקרא "תכנות מכוון עצמים"‬
‫(‪ ,Object Oriented‬או בקיצור ‪)OO‬‬
‫‪ ‬הדגש יהיה על האובייקטים שיש במערכת‪ ,‬מה המידע שיש לכל‬
‫אובייקט‪ ,‬ומה הפעולות שכל אובייקט יודע לבצע‬
‫‪ ‬כמו ‪ struct‬של ‪ ,C‬אבל בנוסף לשדות‪ ,‬יהיו גם פעולות‬
‫‪ ‬מתודולוגיית תכנות זו מייצגת כיצד העולם שלו בנוי‬
‫‪ ‬דוגמא‪:‬‬
‫‪ ‬לסטודנט יהיו את השדות‪ :‬ת‪.‬ז‪ ,‬שם‪ ,‬תאריך לידה‪ ,‬מגמת לימוד‬
‫וממוצע‬
‫‪ ‬בנוסף‪ ,‬הוא ידע לבצע את הפעולות הבאות‪ :‬להירשם לקורס‪ ,‬להדפיס‬
‫את נתוניו‪ ,‬ללמוד למבחן‪ ,‬להכין מטלות וללכת לים‬
‫‪4‬‬
‫‪© Keren Kalif‬‬
‫לתכנות מכוון עצמים ‪ 3‬עקרונות מרכזיים‬
‫‪ .1‬הסתרה )‪ :(encapsulation‬כל הנתונים והפעולות הקשורות‬
‫לישות מסוימת מרוכזות יחדיו‪ .‬המשתמש עובד עם "קופסא‬
‫שחורה"‪.‬‬
‫יתרונות‪ :‬קל ללמוד את הקוד ולהתמצא בו‪ ,‬תחזוקה פשוטה‬
‫‪ .2‬הורשה )‪ :(inheritance‬הרחבה של ישות קיימת כדי למנוע שכפול‬
‫קוד‪ ,‬או לחילופין כדי לתת מימוש אלטרנטיבי לקוד קיים‪.‬‬
‫למשל‪ :‬ל‪ person -‬יש אוסף נתונים‪ ,‬ול‪ student -‬יש בדיוק אותם‬
‫נתונים ועוד כמה נוספים‪ .‬לא נרצה לשכפל את כל הקוד שיש ב‪-‬‬
‫‪..person‬‬
‫‪ .3‬רב‪-‬תצורתיות (פולימורפיזם‪ :)polymorphism ,‬מאפשר להתייחס‬
‫לישויות שונות בעלי בסיס זהה באותו אופן‪.‬‬
‫למשל‪ :‬החזקת מערך של צורות‪ ,‬כאשר חלק מהצורות הן ריבוע‪,‬‬
‫חלקן עיגול או משולש‪ ,‬ולהיות מסוגלים להדפיס את כולן‪.‬‬
‫‪5‬‬
‫‪© Keren Kalif‬‬
‫תכנות מכוון עצמים ו‪++C -‬‬
‫בנוסף ל‪ 3 -‬העקרונות שראינו קודם‪ ,‬בשפת ‪ ++C‬יש ‪ 2‬עקרונות‬
‫נוספים‪:‬‬
‫‪ .1‬תבניות )‪ :(templates‬כלי המאפשר לכתוב קוד כללי לטיפוסים‬
‫שונים‪.‬‬
‫דוגמא‪ :‬האלגוריתם למיון קבוע לכל טיפוס‪ ,‬אבל המערך המתקבל‬
‫ופעולות ההשוואה מבוצעות על טיפוסים שונים‪.‬‬
‫במקום לשכפל את הקוד עבור טיפוס שונה כל פעם‪ ,‬ניתן לכתוב‬
‫פונקציה כללית אחת שתדע לטפל בכל טיפוס‪.‬‬
‫‪ .2‬חריגות )‪ :(exceptions‬מנגנון לטיפול שגיאות בזמן ריצה‪.‬‬
‫‪6‬‬
‫‪© Keren Kalif‬‬
‫מ‪ C -‬ל‪++C -‬‬
‫‪ ‬שפת ‪ ++C‬מאוד דומה סינטקטית לשפת ‪ ,C‬אך יחד עם זאת‬
‫יש כמה שינויים‪:‬‬
‫‪‬‬
‫נפתח פרויקט באותו אופן כמו בקורס ‪ ,C‬אבל נייצר קובץ עם‬
‫סיומת ‪( cpp‬ברירת המחדל)‪ ,‬ולא עם סיומת ‪c‬‬
‫‪ ‬הקומפיילר שונה‪ ,‬ולכן יתכנו שגיאות קומפילציה טיפה שונות‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪7‬‬
‫ניתן להגדיר משתנים בכל חלק בתוכנית‪ ,‬ולא רק בתחילת בלוק‬
‫במקום הכללת הספריה ‪ stdio.h‬שהכילה פקודות קלט‪-‬פלט‪ ,‬נכליל‬
‫את הספריה ‪ iostream‬ונוסיף ;‪( using namespace std‬הסבר‬
‫בהמשך)‬
‫קיים הטיפוס ‪ bool‬שמחזיק את הערכים ‪ true‬או ‪false‬‬
‫‪© Keren Kalif‬‬
‫מ‪ C -‬ל‪)2( ++C -‬‬
‫‪ .5‬פקודות שונות לטיפול בקלט ופלט‪:‬‬
‫‪ ‬במקום הפקודה ‪ printf‬נשתמש בפקודה ‪cout‬‬
‫‪ ‬במקום הפקודה ‪ scanf‬נשתמש בפקודה ‪cin‬‬
‫‪ .6‬פקודות שונות לטיפול בהקצאות ושחרור זכרון‪:‬‬
‫‪ ‬במקום הפקודות ‪ malloc/calloc‬נשתמש בפקודה ‪new‬‬
‫‪ ‬במקום הפקודה ‪ free‬נשתמש בפקודה ‪delete‬‬
‫‪8‬‬
‫‪© Keren Kalif‬‬
..‫ שאני לא אתעצבן‬,‫אבל ראשית‬
http://qph.is.quoracdn.net/main-qimg-e0c9dafb319150b6c6d9816047ed9eae?convert_to_webp=true
© Keren Kalif
9
cout ‫הפקודה‬
(Console OUT) ‫ יודעת להדפיס נתונים למסך‬
‫ אין צורך להבדיל בהדפסה‬:printf -‫ הסינטקס הרבה יותר פשוט מ‬
‫ משרשרים את חלקי המחרוזת להדפסה‬,‫בין הטיפוסים השונים‬
>> ‫באמצעות‬
#include <stdio.h>
#include <iostream>
using namespace std;
void main()
{
char str[] = "hi";
int num = 5;
printf("string: %s number: %d\n",
str, num);
{
void main()
{
char str[] = "hi";
int num = 5;
cout << "string: " << str << " number: "
<< num << "\n";
}
© Keren Kalif
10
cin ‫הפקודה‬
(Console IN) ‫ יודעת לקרוא נתונים מהמקלדת‬
‫ אין צורך להבדיל בקליטה‬:scanf -‫ הסינטקס הרבה יותר פשוט מ‬
‫ משרשרים את הנתונים השונים שרוצים‬,‫בין הטיפוסים השונים‬
<< ‫לקרוא באמצעות‬
#include <stdio.h>
#include <iostream>
using namespace std;
void main()
{
char str[10]
int num;
printf("Enter string and number: ");
scanf("%s%d", str, &num);
{
void main()
{
char str[10];
int num;
cout << "Enter string and number: ";
cin >> str >> num;
}
© Keren Kalif
11
cin.getline ‫הפקודה‬
‫ יודעת לקרוא מחרוזת עד רווח‬cin 
‫ או עד‬ENTER ‫ ניתן לקלוט תווים עד‬cin.getline -‫ ע"י שימוש ב‬
‫למקסימום תווים‬
#include <stdio.h>
#include <iostream>
using namespace std;
void main()
{
char str[10];
printf("Enter string: ");
gets(str);
{
void main()
{
char str[10];
cout << "Enter string: ";
cin.getline(str, 5);
}
‫ תווים‬4 ‫הפונקיה תקרא עד‬
)'\0' -‫(אחד עבור ה‬
ENTER ‫או עד‬
© Keren Kalif
12
delete -‫ ו‬new ‫הפקודות‬
‫ מקצה מערך עם ערכי זבל‬,‫ להקצאה דינאמית‬new ‫ הפקודה‬
)malloc -‫(מקבילה ל‬
calloc -‫ אין מקבילה ל‬
realloc -‫ אין מקבילה ל‬
‫ אם משחררים מערך יש לשחרר‬.‫ לשחרור זכרון‬delete ‫ הפקודה‬
][ ‫עם‬
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
void main()
{
int *arr, numOfElements;
void main()
{
int *arr, numOfElements;
printf("How many elements? ");
scanf("%d", &numOfElements);
arr = (int*)malloc(numOfElements*sizeof(int));
free(arr);
}
cout << "How many elements? ";
cin >> numOfElements;
arr = new int[numOfElements];
delete []arr;
}
© Keren Kalif
13
‫דוגמא‬
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
void main()
{
int i, numOfNumbers;
int* arr;
void main()
{
int numOfNumbers;
printf("How many numbers? ");
scanf("%d", &numOfNumbers);
cout << "How many numbers? ";
cin >> numOfNumbers;
arr = (int*)calloc(numOfNumbers, sizeof(int));
for (i=0 ; i < numOfNumbers ; i++)
printf("Value #%d: %d\n", i+1, arr[i]);
int* arr = new int[numOfNumbers];
for (int i=0 ; i < numOfNumbers ; i++)
cout << "Value #" << i+1 << ": " << arr[i] << "\n";
free(arr);
delete []arr;
}
{
‫נשים לב להגדרת המשתנים באמצע התוכנית‬
© Keren Kalif
14
‫ תוכנית המטפלת במטריצה‬:‫דוגמא‬
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
#include <iostream>
using namespace std;
const int N = 3;
const int M = 4;
// prototypes
int** allocateMatrix
void enterInput
void printMatrix
void freeMatrix
(int rows, int cols);
(int** mat, int rows, int cols);
(int** mat, int rows, int cols);
(int** mat, int rows);
void main()
{
int** mat = NULL;
‫ כתוב בראשי פרקים וניתן‬main -‫ה‬
‫להבין בקלות מה קורה בתוכנית‬
mat = allocateMatrix(N, M);
enterInput(mat, N, M);
printMatrix(mat, N, M);
freeMatrix(mat, N);
{
© Keren Kalif
15
)2( ‫ תוכנית המטפלת במטריצה‬:‫דוגמא‬
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
int** allocateMatrix(int rows, int cols)
‫ עבור השורות‬,int* ‫בתחילה מקצים מערך של‬
}
int** mat = new int*[rows]; // allocating the rows
for (int i=0 ; i < rows ; i++)
mat[i] = new int[cols]; // allocating the columns
return mat;
{
void enterInput(int** mat, int rows, int cols)
}
for (int i=0 ; i < rows ; i++)
for (int j=0 ; j < cols ; j++)
mat[i][j] = i*cols + j;
{
‫נשים לב לאפשרות הגדרת‬
‫המשתנים בכל חלק בקוד‬
© Keren Kalif
16
)3( ‫ תוכנית המטפלת במטריצה‬:‫דוגמא‬
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
void printMatrix(int** mat, int rows, int cols)
{
for (int i=0 ; i < rows ; i++)
}
for (int j=0 ; j < cols ; j++)
cout << mat[i][j] << ", ";
cout << "\b\b \n";
{
{
void freeMatrix(int** mat, int rows)
}
for (int i=0 ; i < rows ; i++)
delete []mat[i];
] [ ‫ שחרור מערך עם ציון‬:‫נשים לב‬
delete []mat;
{
© Keren Kalif
17
‫טיפוס התייחסות‬
‫‪ ‬בשפת ‪ C‬כאשר רצינו שפונקציה תשנה את ערכו של ארגומנט‬
‫מסוים‪ ,‬העברנו את הכתובת שלו (העברה ‪ ,by pointer‬לעומת‬
‫העברת העתק‪)by value ,‬‬
‫‪ ‬בשפת ‪ ++C‬עדיין ניתן להעביר מצביעים כדי לשנות ארוגמנטים‬
‫בפונקציות‪ ,‬אך יש דרך חדשה הנקראית העברת פרמטרים ‪by‬‬
‫‪reference‬‬
‫‪ ‬בהגדרת הפרמטרים שהשיטה מקבלת מציינים ליד הפרמטר &‬
‫‪ ‬זהו למעשה מתן שם נוסף לארגומנט המקורי שנשלח הנגיש מתוך‬
‫הפונקציה‬
‫‪ ‬אין הבדל סינטקטי בשליחת המשתנה בעבור משתנה שעובר ‪by‬‬
‫‪ value‬או ‪by reference‬‬
‫‪18‬‬
‫‪© Keren Kalif‬‬
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
void changeTo4byVal(int x)
{
x = 4;
{
by ref ‫שליחת פרמטר‬
by val ‫לעומת‬
void changeTo4byRef(int& x)
}
x = 4;
}
10
4
int: x
chagneTo4ByVal -‫הזכרון של ה‬
int: x
void main()
}
int num = 10;
cout << "orig num = " << num << endl;
2000
???
chagneTo4ByRef -‫הזכרון של ה‬
4
10
int: num
1000
main -‫הזכרון של ה‬
changeTo4byVal(num);
cout << "after changeTo4byVal: num = " << num << endl;
changeTo4byRef(num);
cout << "after changeTo4byRef: num = " << num << endl;
{
© Keren Kalif
19
swap ‫הדוגמא‬
#include <iostream>
using namespace std;
void swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
{
void main()
}
int x=3, y=5;
int: a
???
int: b
???
int: temp
???
3
2000
swap -‫הזכרון של ה‬
int: x
???
3
5
1000
int: y
???
5
3
1004
main -‫הזכרון של ה‬
cout << "Before swap: x = " << x << ", y = " << y << "\n";
swap(x, y);
cout << "After swap: x = " << x << ", y = " << y << "\n";
{
© Keren Kalif
20
reference ‫משתנה מטיפוס‬
‫מתן שם נוסף למשתנה כלשהו‬
‫חייב להיות מאותחל‬
‫לא ניתן לייצר מערך של הפניות‬
‫ ולכן כתובתו כמו כתובת המשתנה אליו‬,‫אינו תופס מקום נוסף‬
void main()
‫הוא מפנה‬
{
int x = 5, y = 3;
int& ref = x;
int: x
???
5
6
3
4
1000
int: y
???
3
1004




int&: ref
cout << &ref << " " << &x << endl;
cout << "x=" << x << ", y=" << y << ", ref=" << ref << endl;
x++;
cout << "x=" << x << ", y=" << y << ", ref=" << ref << endl;
ref = y;
cout << "x=" << x << ", y=" << y << ", ref=" << ref << endl;
ref++;
cout << "x=" << x << ", y=" << y << ", ref=" << ref << endl;
}
main -‫הזכרון של ה‬
© Keren Kalif
21
‫החזרת טיפוס התייחסות מפונקציה‬
‫‪ ‬בשפת ‪ C‬יכולנו להחזיר כתובת של משתנה מתוך פונקציה‪ ,‬וכך‬
‫למעשה החזרנו את המשתנה המקורי‪ ,‬ולא העתק שלו‬
‫‪ ‬בשפת ‪ ++C‬עדיין ניתן להחזיר משתנה ‪ ,by pointer‬אך ניתן גם‬
‫להחזיר משתנה ‪by reference‬‬
‫‪ ‬יש לשים לב שכשמחזירים משתנה ‪ by reference‬שהוא עדיין‬
‫יהיה קיים ביציאה מהפונקציה (בדיוק כמו בשפת ‪)C‬‬
‫‪22‬‬
‫‪© Keren Kalif‬‬
23
© Keren Kalif
‫ מפונקציה‬by ref ‫דוגמא להחזרת משתנה‬
#include <iostream>
using namespace std;
void printArr(int arr[], int size)
{
for (int i=0 ; i < size ; i++)
cout << arr[i] << " ";
cout << endl;
{
void main()
}
int arr[] = {6,8,2};
int size = sizeof(arr)/sizeof(arr[0]);
getMax(arr, size) *= 10;
cout << "arr after changing max:\n ";
printArr(arr, size);
{
int[]: arr
‫הפונקציה מחזירה הפניה לאיבר המקסימלי‬
int& getMax(int arr[], int size)
}
int maxIndex = 0;
for (int i=1 ; i < size ; i++)
if (arr[i] > arr[maxIndex])
maxIndex = i;
return arr[maxIndex];
{
int: size
???
6
1000
???
80
8
1004
???
2
1008
???
3
1012
main -‫הזכרון של ה‬
int[]: arr
1000
1000
int: size
3
1004
???
1
1012
getMax -‫הזכרון של ה‬
int: maxIndex
‫מרחבי שמות )‪(namespace‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫כאשר עובדים על פרויקט גדול‪ ,‬לרוב משתמשים בקוד מוכן‪ ,‬וכל‬
‫מתכנת כותב את חלקו‬
‫יתכן מצב שיהיו ‪ 2‬פונקציות בעלות שם זהה המקבלות את אותם‬
‫נתונים‬
‫תיוצר בעיה של התנגשות בשמות‪ ,‬הקומפיילר לא ידע לאיזה‬
‫פונקציה לפנות‬
‫הפתרון‪ :‬שימוש ב‪namespace -‬‬
‫‪ ‬השימוש ב‪ namespace -‬מאפשר קישור של פונקציה מסוימת‬
‫לחבילת קוד מסוימת‬
‫‪24‬‬
‫‪© Keren Kalif‬‬
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
#include <iostream>
using namespace std;
:‫דוגמא‬
namespace -‫שימוש ב‬
3 ‫להלן קטע קוד עם‬
namespace first
‫פונקציות זהות‬
{
void foo() }cout << "This is the first foo\n“; { ‫ מימושים נמצאים בתוך‬2
{
‫ שונה‬namespace
namespace second
‫פניה לפונקציה הנמצאת‬
}
namespace ‫בתוך‬
void foo() }cout << "This is the second foo\n“; {
-‫מחייבת ציון שם ה‬
{
‫ שבתוכו‬namespace
void foo() }cout << "This is just foo\n“; {
‫היא נמצאת‬
void main()
‫פונקציה שלא בתוך‬
{
first::foo();
‫ נמצאת‬napespace
second::foo();
‫במרחב השמות הגלובלי‬
foo();
{
© Keren Kalif




25
namespace -‫קיצור אופן השימוש ב‬
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
#include <iostream>
using namespace std;
namespace first
{
void foo() {cout << "This is the first foo\n";}
}
namespace second
}
void foo() {cout << "This is the second foo\n";}
{
using namespace second;
void main()
}
first::foo();
second::foo();
foo();
{
‫פקודה זו מאפשרת לנו לפנות לפונקציות‬
‫ זה בלי הקידומת‬namespace ‫שתחת‬
© Keren Kalif
26
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
‫קיצור אופן השימוש‬
namespace -‫ב‬
#include <iostream>
using namespace std;
namespace first
{
void foo() {cout << "This is the first foo\n";}
}
namespace second
}
void foo() {cout << "This is the second foo\n";}
{
void foo()
}cout << "This is just foo\n“; {
using namespace first;
using namespace second;
void main()
}
first::foo();
second::foo();
foo(); // ERROR!
::foo();
{
‫במקרה זה נהייה חייבים תמיד לפנות‬
‫ אחרת נקבל‬,‫בשם המלא של הפונקציה‬
ambiguous call to :‫את השגיאה‬
‫ מאחר והקומפיילר‬overloaded function
‫לפנות‬
‫פונקציהלפנות‬
‫לאיזו פונקציה‬
‫יודע איזו‬
‫אינו יודע‬
‫אינו‬
‫פניה בשם המלא לפונקציה‬
‫הנמצאת במרחב השמות הגלובלי‬
© Keren Kalif
27
?using namespace std ‫מדוע שמים את‬
‫ זה יש את כל הפקודות הבסיסיות‬namespace ‫ בתוך‬
‫ לכל הפונקציות‬::std ‫ בלעדיו נצטרך להוסיף את הקידומת‬
:‫ אחרת נקבל למשל את השגיאה‬,‫הבסיסיות שבהן נשתמש‬
error C2065: 'cout' : undeclared identifier
#include <iostream>
using namespace std;
#include <iostream>
void main()
{
int x;
void main()
{
int x;
cout << "Enter a number: ";
cin >> x;
{
std::cout << "Enter a number: ";
std::cin >> x;
{
© Keren Kalif
28
:‫ביחידה זו למדנו‬
‫ הגדרת תכנות מכוון עצמים‬
:++C -‫ ל‬C ‫ השוני הסינטקטי בין‬
printf  cout 
scanf  cin 
gets  cin.getline 
malloc  new 
free  delete 
‫ טיפוס התייחסות‬
)namespace( ‫ מרחבי שמות‬
© Keren Kalif
29

similar documents