1
++C יוצרים, הורסים :ואופרטורים
2
Stackחלקה המ זכורתת
class Stack {
private:
int* array;
int size, top_index;
public:
Result init (int size) ;
void destroy() ;
Result push (int e);
Result pop ();
Result top(int& e);
Result print() ;
} ;
class Stack {
private:
int* array;
int size, top_index;
public:
Result init (int size) ;
void destroy() ;
Result push (int e);
Result pop ();
Result top(int& e);
Result print() ;
} ;
init ומה יקרה אם המשתמש ישכח לקרוא ל-destroyאו ל-???
3
יוצרים והורסים פתח לטעויות- בהגדרת הטיפוס אנו משאירים הבעיה
של המשתמש היכול לשכוח לאתחל אובייקטים לפני ) דבר העלול לגרום initהשימוש בהם (לא לקרוא ל
לכך שהתוכנית תתנהג באופן בלתי צפוי, או לשכוח להרוס אובייקט וכך לגרום לדליפת זיכרון.
אובייקטים זמניםיתר על כן, פעמים רבות נוצרים (למשל, בהעברת והחזרת פרמטרים מפונקציות) שאין
למשתמש יכולות לאתחלם בצורה מפורשת.
פונקציות אתחול והריסה שנקראות – הפתרוןאוטומטית כאשר האובייקט נוצר וכאשר הוא אמור
להיהרס.
נדרש Stackבכל פעם שנגדיר משתנה מטיפוס מעתה,להעביר ליוצר את הפרמטרים הדרושים לצורך אתחול
האובייקט, ובכל פעם שהאובייקט לא יהיה נגיש יותר הקומפיילר יקרא להורס אשר ישחרר את האובייקט.
) כשם המחלקה. שם constructorsשמות היוצרים () כשם המחלקה שלפניה ~.destructorההורס (
class Stack {
private:
int* array;
int size, top_index ;
public:
Stack (int size) ; Stack (int size) ;
~Stack() ;~Stack() ;
…
4
Stack::Stack (int s) {
array = new int[s] ; // חסרה בדיקה
top_index = 0; size = s ;
}
Stack::~Stack() {
delete[] array;
}
Destructors ו Constructorsמימוש
לא מחזירים ערכים !D’tor ו C’torשימו לב
הערה:
כמעט לכל מחלקה נדרש להגדיר יוצרים לאתחול שדותיה.
הגדרת הורסים תתבצע בד”כ עבור מחלקות המקצות זיכרון דינמי בעצמן. במקרה זה על
ההורס לשחררו.
אם איננו מגדירים יוצרים והורסים, מוגדרים אוטומטית יוצרים והורסים דיפולטיים.
5
שימוש במחסנית שיש לה יוצרים והורסים#include “Stack.h”
int main() {
int i;
Stack s(100) ; // the c’tor is called with size 100
Stack* ps = new Stack(100); // same here
if (ps == NULL) exit(1);
s.push (1); s.push(213); s.pop (); s.top(i);
ps->push(1); ps->push(2); ps->pop() ;
delete ps ; // the d’tor is called for ps
}
// the d’tor is called for s
6
זמני קריאה של יוצרים והורסים דרכים להקצאת משתנים. זמני הקריאה של 4קיימות •
היוצרים וההורסים תלויים באופן שבו הוקצה האובייקט.
משתנים לוקאלייםבכל פעם שהתוכנית נקראהיוצר
מגיעה להכרזת המשתנה. בכל פעם שהתוכנית ההורס
יוצאת מהתחום בו הוגדר המשתנה
משתנים גלובאלייםעם תחילת התוכנית נקראהיוצר
).main(לפני ה- עם סיום התוכנית (לאחר ההורס
).mainסיום
משתנים דינמייםבכל פעם שמוקצה נקראהיוצר
. newאובייקט ע”י בכל פעם שאובייקט ההורס
.deleteמשוחרר ע”י
משתנים סטאטייםבפעם הראשונה נקראהיוצר
שהתוכנית מגיע לתחום בו מוגדר המשתנה.
עם סיום התוכנית.ההורס
7
דוגמא לזמני קריאה#include “Stack.h”
Stack glbals(100); // globals c’tor is called
int main() {
Stack locals(50) ; // locals c’tor is called
Stack* ps = new Stack(600); // ps c’tor is called
Stack* ps2 = new Stack(600); // ps2 c’tor is called
delete ps ; // ps destructor is called
return 0; // locals destructor is called
}
// globals destructor is called
// ps2 destructor is never called !
8
Advanced C’tors and D’tors
class TwoStack .. {Stack s1, s2 ;
public :TwoStack(int s) ;
..};
TwoStack::TwoStack (int size) :s1(100), s2(size * 5) { …
}
של מערכיםבכדי להקצות •אובייקטים ממחלקה כלשהי, לאותה
מחלקה נדרש שיהיה יוצר שלא מקבל פרמטרים (או שיש לו ערכים דיפולטיים לכל הפרמטרים שלו כך
שאין צורך לציין פרמטרים במפורש).
ניתן להגדיר מחלקות אשר יש בהן •שדות שהם עצמם עצמים של
מחלקות אחרות. במקרה זה:כאשר נוצר אובייקט של –
המחלקה, ראשית מאותחלים כל שדותיו. כאשר נהרס אובייקט
כזה, ראשית נקרא ההורס שלו ורק אח”כ של שדותיו.
אם יוצרי השדות זקוקים –לפרמטרים ניתן לבצע זאת ע”י
רשימת אתחול.
9
רשימות אתחולהדרך המקובלת לאתחול שדות פנימיים של מחלקה. •מייד אחרי הצהרת היוצר ולפני גוף היוצר (החלק שבתוך ה-{}) •
מופיעות נקודותיים ואז רשימה של השדות הפנימיים, כשהם מופרדים על ידי פסיקים.
כל מופע של שדה ברשימת האתחול הוא למעשה קריאה לאחד •מהיוצרים של השדה.
לכל שדה כותבים בסוגריים את הערכים שמעבירים ליוצר שלו •(ויכולים להיות תלויים בפרמטרים שהועברו ליוצר הראשי).
הסדר בו הם מופיעים ברשימת האתחול אינוסדר הפעלת היוצרים •אלא בו השדות מופיעים בהגדרת המחלקה.
רשימת אתחול כדאית על פני "אתחול" בתוך הפונקציה כיוון •שחוסכים אתחול ראשוני בזבל.
10
operators overloading לאפשר עבודה טבעית ככל האפשר עם מחלקות שהגדיר מטרה:•
המשתמש.
ע”י שימוש באופרטורים, המחלקות שהמשתמש מגדיר יוכלו לעבוד עם אופרטורים כמו טיפוסים פנימיים. אם האופרטורים מממשים פעולה
טבעית של המחלקה, הדבר יכול לשפר את הקריאות של הקוד. הגדרת אופרטורים מתבצעת באופן הבא:•
<return type> operator<operator name> (<arguments>);
של המחלקה או פונקציות methodsאופרטורים יכולים להיות •חיצוניות.
אזי הארגומנט הראשון הנו תמיד methodאם האופרטור מוגדר כ • להעבירו.ואין צורךהאובייקט של המחלקה בו הוא מוגדר
11
operators overloading
operatorפונקציות עם שם מיוחד: + •Stack& operator+=(int e) { // bad : no error checking !
array[top_index] = e; top_index++;
return *this
}
צורת קריאה לפונקציה: • s.operator+=(5) ; s += 5 ;
12
The matrix exampleclass M {
double mat[5][5];public:
...M& operator+=(const M&);friend M operator+( const M&,
const M&);M operator*(const M &) ;
…}; M operator+(const M&,const M&){
…
int main() {
M m1 , m2, m3 ;
….
m1+= m2 ;
// calls m1.operator+=(m2);
m2 = m1 + m3 ;
// calls operator+(m1,m3);
m3 = m2 * m2;
// calls m2.operator*(m2);
}
13
operators overloading - what can we overload?
+ - * / % ^ & | !
= < > += -= *= /= %=
^= &= |= << >> <<= >>= ==
!= <= >= && || ++ -- ,
->* -> () []
what not ? . .* :: ? : sizeof
post & pre
14
Operators overloadingמגבלות
רק אופרטורים שכבר קיימים (ולא למשל אופרטור $%$).•
האופרטורים מקבלים את אותו מספר משתנים•
אותו סדר עדיפות ואותה אסוציאטיביות.•
הערות בנוגע להגדרת אופרטורים:
ניתן להגדיר את שני סוגי הגדלה / הקטנה עצמית ע”י .בפרמטר דמישמוש
• x.operator++() (pre) (++num)
• x.operator++(int) (post) (num++)
15
ערכי החזרה של אופרטורים יחזיר בדרך כלל איבר זמני classאופרטור עבור המחלקה
.&class או רפרנס לאובייקט שעליו נקרא: classמסוג
יוצרת איבר חדש כאשר הפעולה איבר זמניהאופרטור יחזיר .a+bשאותו אנו רוצים לקבל, למשל
רוצים לקבל את כאשר אנו רפרנסהאופרטור יחזיר דורש שהפעולה a+=2=+(3(. למשל, האובייקט לאחר השינוי
תחזיר רפרנס (ואז ניתן יהיה להפעיל אותה מספר פעמים +=ברצף על אותו אובייקט).
אמורה a++ אמורה להחזיר איבר זמני, ואילו ++aהפעולה להחזיר רפרנס (מדוע?)
16
Overloading של אופרטורי I/Oהקלט והפלט בכדי לאפשר של אופרטוריoverloadניתן לבצע
יקטים מטיפוסים שהוגדרו ע"י י מאובטלבצע קלט אל ופלהמשתמש בדומה לצורה שבה הדבר אפשרי עבור טיפוסים
פנימיים.
int main
{
int x;
someclass y;
otherclass z;
cin >> x >> y >> z ;
cout << x << y << z;
}
17
Overloading של אופרטורי I/Oclass someclass { int k , j ; ….
friend ostream& operator<< (ostream& os, const someclass& s1);friend istream& operator>> (istream& is, someclass& s1);
};ostream& operator<< (ostream& os,const someclass& s1) {
os << “(“ << s1.k << “,” << s1.j <<“)” ;return os ;
}istream& operator>> (istream& is, someclass& s1) {
is >> s1.k >> s1.j ;return is ;
}כיצד ניתן האם זה מתחייב? (רמז: לא). ?friendלמה האופרטורים הוגדרו כ •
? לשנות זאת
18
overloading של אופורטורי I/Oint main()
{
someclass s;
int i; ….
cin >> i >> s ; // assume the user typed 1, 2 and 3
cout << s << i; // What does the program print?
}
להגדיר • ניתן לא האלה האופרטורים את כי לב שימו צריך memberכפונקצית שלו הראשון שהארגומנט מכיוון
עצם השייך למחלקה אחרת )ערוץ(.על הפונקציה המחזירה את האופרטור להחזיר כפלט את •
הערוץ כדי שיהיה אפשר להמשיך ולהזרים לו נתונים.
19
class String {int len; // length of stringchar* val; // value of stringvoid enter(const char*);
public:String(); // String x;String(const char*); // String x = "abc”String(const String&); // String x = y;String& operator=(const String&); // x = y;String& operator=(const char*); // x = “abc”;~String();
int len() const { return len; } // x.len()char operator[](int i) const ; // c = x[1]char& operator[](int i); // x[1] = ‘d’ String& operator+=(const String&); // x+=y
friend bool operator==(const String&, const String&); // if (x == y)friend bool operator!=(const String&, const String&); // if (x != y)friend ostream& operator<<(ostream&, const String&); // cout << x;
};
String.h
בתרגול הבא
20
#include <cstdlib.h>error(const char* st) {
cerr << st << '\n';exit (1);
}void String::enter(const char* st) {
if (val) delete[] val; // if string existsif (st == 0) // Null pointer
val = 0 ;else
if (val = new char[strlen(st)+1]) {strcpy(val, st);len = strlen(st);
}else error("String: out of memory");
}
String.cc (1)
21
String::String() {
len = 0; val = 0; // Null pointer
}
String::String(const char* st) {
len = 0; val = 0;
enter(st);
}
String::String(const String& st) {
len = 0; val = 0;
enter(st.val);
}
String::~String() {
if (val) delete[] val;
}
String.cc (2)
0 ל valלמה קובעים את ?enterלפני הקריאה ל
22
String& String::operator=(const char* st) {enter(st);return *this;
}
String& String::operator=(const String& st){if (this!= &st)
enter(st.val);return *this;
}
String.cc (3)
בדיקת הצבה עצמית
23
char& String::operator[](int i) {if ((i < 0) || (i > len))
error("String: index out of range");return val[i];
}
char String::operator[](int i) const {if ((i < 0) || (i > len))
error("String: index out of range");return val[i];
}
String.cc (4)
24
String& String::operator+=(const String& st) {
char* p;
if (st.len == 0)
return *this ;
if ( (p = new char[len+st.len+1]) == 0)
error (“String: out of memory”);
strcpy(p,val);
strcat(p,st.val);
enter(p);
delete[] p;
return *this ;
}
String.cc (5)
25
bool operator==(const String& x, const String& y){
return !strcmp(x.val, y.val) ;
}
bool operator!=(const String& x, const String& y) {
return !(x == y);
}
ostream& operator<<(ostream& os,const String& st) {
if(st.val)
os << st.val;
return os;
}
String.cc (6)
26
String תוכנית הנעזרת ב int main() {
String a;String b(“1234");String c = b; // Copy constructor
a = "kookoo"; // operator=(const char*) is calledcout << a << "\n"; // kookoo is printeda = c; // operator=(const String&) is calledcout << a << "\n"; // 1234 is printeda+=b;cout << a << "\n"; // 12341234 is printedc="oooo"; // If the copy constructor or assignment
// were not redefined, b or a would be// changed as well
}
27
Class String: important points
שימוש בפונקציה אחת שעושה את כל העבודה •) וכל השאר רק קוראות לה. enterהאמיתית בבניה (
דבר זה מאפשר גמישות למשתמש במחלקה תוך הימנעות מכתיבת קוד מרובה אצל המיישם.
.method הינו חלק מחתימת ה constהעובדה כי •
מדוע האופרטורים == ו =! הינם פונקציות גלובליות • .methodsבעוד =+ ו [] הינם
אופרטור הפלט. •
28
Class String: points to improve
c’torsלא השתמשנו ברשימת אתחול ב-•
String::string(): val(0), len(0){}יותר מדיי פונקציות חברות, מחלקה צריכה להיות •
כמה שיותר סוציומטית. אפשר להוסיף פונקציות פנימיות נוספות על מנת שפונקציות ההשוואה לא
יצטרכו לגשת לשדות.;char operator[] (int i) const :נחליף את•
const char& operator[](int i) const;ב-
29
מומלץ מאוד!!!
http://www.yanivhamo.com/material/yanivs_string.pdf