1 נושאי התרגול : copy constructorcopy constructor assignment operatorassignment operator...

31
1 ללללל לללללל: Copy constructor Copy constructor Assignment operator Assignment operator לללל ללללללל ללללללללל לללל ללללללל ללללללללל ללללל לללל – ללללל לללל –Templates Templates

Post on 21-Dec-2015

223 views

Category:

Documents


6 download

TRANSCRIPT

1

:נושאי התרגול

•Copy constructorCopy constructor

•Assignment operatorAssignment operator

המרת טיפוסים המרת טיפוסים •אוטומטיותאוטומטיות

TemplatesTemplatesתכנות גנרי – תכנות גנרי – •

2

Copy constructorCopy constructorAssignment operatorAssignment operator

3

Copy constructor & Assignment operator

?s1 = s2מה קורה כאשר מבצעים •?Stack s1 = s2מה קורה כאשר מבצעים •

מה ההבדל? מה הסכנה?מה קורה כאשר קוראים לפונקציה:•

f(Stack s) ;f(Stack& s) ;

- להשמה.assign. Op - לאתחול, copy c’torפתרון •

4

Copy constructor & Assignment operator

שתי אוטומטי באופן מוגדרות חדשה, מחלקה כל עבור :פונקציות

•copy constructor –משמש לאתחול של עצם אחד של המחלקה ע"י עצם –

אחר של אותה מחלקהבקריאה לפונקציות, כלומר משמש לאתחל פרמטרים–

byכאשר מעבירים פרמטר מסוג מחלקה מסוימת value ערכי השדות של הפרמטרים מאותחלים ע"י ,

קריאה לפונקציה זאת )כנ"ל לגבי החזרת ערכים(

X(const X&)X(const X&)

מובטח שלא נשנה את הפרמטר המועבר

copy constructorאי אפשר להגדיר copy constructorבאמצעות

5

– אופרטור ההשמה )=(•משמש להשמה של עצמים מהמחלקה זה לזה פרט –

לאתחול של עצם אחד של המחלקה ע"י עצם אחר copyשל אותה מחלקה )במקרה של אתחול ייקרא ה-

c’tor)אפילו אם משתמשים בסימן = בביצוע האתחול ,

בשני המקרים, בפונקצית ברירת המחדל •ההעתקה היא איבר איבר. יש מקרים שבהם

העתקה איבר איבר אינה טובה )למשל, במקרה בו יש מצביעים כשדות במחלקה(. במקרים האלה, על המשתמש להגדיר פונקציות אלו

מחדש

Copy constructor & Assignment operator

6

Copy constructor & Assignment operator

כלל אצבע: בכל מחלקה בה יש שדות •, יש להגדיר מחדש:מצביעיםשהם

• Copy Constructor

• Assignment Operator

• Destructor

7

The Final Stack

class Stack {

int* array; int top_index, size;

public:

Stack (int s = 100) ;

Stack (const Stack&) ;

Stack& operator=(const Stack&);

~Stack() ;

Result push(int e);

Result pop() ;

Result top(int& e);

void print() ;

};

8

Copy constructor

Stack::Stack(const Stack & st) {

array = new int [st.size];

if (array == 0)

error(“out of memory”); // assumes error exits

size = st.size;

top_index = st.top_index;

for (int top = 0 ; top<st.top_index; top++)

array[top] = st.array[top];

}

9

Assignment operatorStack& Stack::operator=(const Stack& st)

{if (this == &st) return *this;

if (st.size != size) {

delete[] array;

array = new int[size = st.size];

}

for (int top = 0 ; top<st.top; top++)

array[top] = st.array[top];

top_index = top;

return *this ;

}

חמשת הצעדים:בדיקת הצבה

עצמית

שחרור משאבים

הקצאת משאבים חדשים

אתחול השדות

*thisהחזרת

10

המרת טיפוסים המרת טיפוסים אוטומטיותאוטומטיות

11

User Defined Type Conversions

קיימים שני סוגי המרות טיפוסים Cב ++אוטומטיות :

• Conversion by constructor

• Conversion by conversion operator

12

Conversion by constructor

אפשר להמיר כל מחלקה T של מחלקה c’torsבעזרת •אחרת או טיפוסים פנימיים של השפה כך שיהפכו לאיבר

Tמסוג המקבל Constructorההמרה מתבצעת ע”י הגדרת •

ארגומנט יחיד, מהטיפוס הנ”ל. T)class C(; //converts from class C to T

על ידי הקומפיילראוטומטיתההמרה תתבצע •ההמרה מאפשרת לקרוא לפונקציות המצפות לקבל •

במקוםC עם ארגומנט מטיפוס Tארגומנט מטיפוס דוגמא:

)עוד מעט(double : המרה מ-Complexבמחלקה – )תרגול String למחלקה const char המרה מ-* –

קודם(

13

Complexדוגמא - המחלקה נירצה להגדיר מחלקה של מספרים מרוכבים. נירצה גם לאפשר את

פעולות החשבון הרגילות בין מספרים מרוכבים ומספרים שלמים. דרך אחת לעשות זאת היא להגדיר את כל האפשרויות עבור כל אופרטור,

בדומה לדוגמא הבאה:class complex {

double re,im;public:

complex )double r, double i( {re = r; im = i;}friend complex operator+)complex, complex(;friend complex operator+)complex, double(;

friend complex operator+)double, complex(;

// Do the same for the rest of the operators…};

זו מייגעת ומלאה חזרות. דרך נוספת לעשות את אותו הדבר היא שיטה ל-constructorלהגדיר complex מטיפוס יחיד פרמטר מקבל אשר double כעת ההמרה תתבצע אוטומטית באמצעות .constructorזה

14

Complexדוגמא - המחלקה class complex {

//...

complex(double r) {re = r; im = 0;}

friend complex operator+(complex,complex);

};

המהדר יבצע את ההמרה באופן אוטומטי. לדוגמא:main()

{

complex a;

complex b;

a=b+23;

}

: מה שמתבצע זה.constructor ע"י ה- complex מומר ל- 1.23.complexמתבצע חיבור בין שני .23.a אופרטור מקבל את התוצאה ע"י

המוגדר כברירת מחדל ע"י ההשמההמהדר )העתקה איבר איבר(.

15

Conversion by conversion operator אפשר להמיר את Tבעזרת אופרטורים מיוחדים שמוגדרים במחלקה •

T לטיפוסים פנימיים של השפה או למחלקות אחרות שכבר הוגדרו הדרך היחידה להגדיר המרה לטיפוסים פנימיים של השפה )למה?(• )מתודה של conversion operatorהמרה זו מבוצעת ע”י הגדרת •

(Tהמחלקה

operator C() ; // converts from T to C.דוגמא: •

class Price { int Shekel, agorot ; …

public: …. operator double() { return shekel + agorot / 100.0 ;} …

}; בתוכו returnשימו לב שלאופרטור אין ערך החזרה אבל השתמשנו ב-•

)אז מה כן מחזירים?(++Cעם ערך החזרה... מנפלאות הסינטקס של

16

בעיות עם המרות אוטומטיותclass x { // ...

x(int); x(char*); … };

class y{ //...

y(int); …. };

class z {//...

z(x); …. };

x f(x);

y f(y);

z g(z);

void k1() {

f(1); // Illegal: ambiguous f(x(1)) or f(y(1))

f(x(1));

f(y(1));

g("asdf") ; // Illegal: g(z(x("asdf"))) not tried

g(z("asdf")); // O.k.: x(char*) is carried out

}

פרט לכך קיימות שתי בעיות עם המרות: מתבצעת רק המרה אחת–(ambiguity) עלולה להיווצר דו משמעות –

17

המרות ואופרטורים הם בעצם פונקציות. לכן גם עבורם יכולה להתבצע operatorsכזכור •

יכול להיקרא:Complexהמרה. לדוגמה, אופרטור החיבור במחלקה c2 = c1 + 5 ;

לא תתבצע המרה על הארגומנט method אולם אם אופרטור מוגדר כ הראשון שלו. בגלל בעיה זו, עדיף בד"כ להגדיר אופרטור ע"י פונק'

חיצונית. כך, הטיפול בשני האופרנדים יהיה סימטריclass complex { ...public: complex)double r( {re = r; im = 0;} operator+)const complex&(; friend operator-)const complex&, const complex&(;};main)( {

complex c2,c1; c1=2.0+c2; // Error: 2.0 will not be converted

c2=2.0-c2; // O.k.: 2.0 will be converted}

18

TemplatesTemplatesתכנות גנרי – תכנות גנרי –

19

Templatesתכנות גנרי -

- מחלקות ContainersContainersמשמש בעיקר כאשר בונים •אשר מכילות עצמים אחרים, אולם אין חשיבות

לתכונותיו הייחודיות של אותו עצם ללא קשר Containerאותן פעולות מוגדרות על ה •

לעצם שבו. לדוגמא עבור מחסנית תמיד יוגדרו :– push– pop– top

String או int , charללא חשיבות אם זו מחסנית של

20

דוגמת המחסניתint_stack.h:class int_stack {

int top_index,size;int* array ;

public:int_stack(int s) ; void pop() ;void push(int e);int top() ;int size();

};

char_stack.h:class char_stack {

int top_index,size;char* array ;

public:char_stack(int s) ; void pop() ;void push(char e);char top() ;int size();

};

String_stack.h:

class string_stack {

int top_index,size;

String* array ;

public:

string_stack(int s) ;

void pop() ;

void push(String e);

String top() ;

int size();

};

21

int_stack.cc (חלקי):int_stack::int_stack (int s) {

top_index = 0 ; size = s ; array = new int[s];

}

int int_stack::top() {return array[top_index-1];

}

string_stack.cc (חלקי):string_stack::string_stack (int s){

top_index = 0 ; size = s ; array = new String[s];

}

String string_stack::top() {return array[top_index-1];

}

דוגמת המחסנית

22

בכדי להימנע משכפול הקוד )ליתר דיוק: כדי לבצע •TemplateTemplateשכפול קוד מבוקר( נעזר ב

מקבל טיפוס)ים( כפרמטר, ויכול Templateה-•להשתמש בפרמטר זה כאילו היה טיפוס רגיל

הנו בעצם תבנית ליצירת מחלקות Templateה •)מעין “מקרו” מתוחכם(. לאחר שהעברנו

את הטיפוס עמו הוא עובד )ע”י Templateל- נקבלTemplateהוספת > טיפוס < לשם ה-

מחלקה רגילה לכל דבר

מחלקות גנריות

23

מה קורה בפועל? כמו שאנחנו כותבים Templateכותבים מחלקה שהיא •

>template <class Tמחלקה רגילה, פרט לתוספת השורה לפני הגדרת המחלקה

כעת נוכל לכתוב את קוד המחלקה תוך שאנחנו מתייחסים • כאל טיפוס כלשהו, שיועבר כפרמטר רק מאוחר יותרTל-

עבור Templateבתוך התוכנית, כאשר נירצה להשתמש ב-•, נעביר את ערך זה גם כן בסוגריים Tערך מסוים של

משולשים )ראו דוגמאות בהמשך(בזמן הקומפילציה יתבצע שכפול של הקוד שכתבנו עבור •

ייכתב הערך שהועבר T ובכל מקום שבו מופיע Templateה- במקומוTemplateל-

24

מחסנית גנריתstack.h:template <class T>class stack {

int top_index,size;T* array ;

public:stack(int s=100) ; void pop() ;void push(T e);T top() ;int size();

};

int_stack.h:

class int_stack {

int top_index,size;

int* array ;

public:

int_stack(int s) ;

void pop() ;

void push(int e);

int top() ;

int size();

};

25

מחסנית גנריתstack.h (חלקי):

template <class T>

stack<T>::stack (int s) {

top_index = 0 ; size = s ;

array = new T[s];

}

template <class T>

T

stack<T>:: top() {

return array[top_index-1];

}

int_stack.cc (חלקי):

int_stack::int_stack (int s) {

top_index = 0 ; size = s ;

array = new int[s];

}

int

int_stack::top() {

return array[top_index-1];

}

26

שימוש במחסנית הגנרית

int main() {stack<int> si(100);stack<char*> sc(3);

si.push(5);sc.push(“xxx”);sc.push(6); // error !si.size();sc.size();

}

int main() {int_stack si(100);

si.push(5);

si.push(“xxx”); // error !si.size();

}

27

Templates

•Templates הינם כלי מאוד שמושי לצורך Containersהגדרת

ב +Templatesלמרבה הצער המימוש של •+C מזכיר macro חכם ולכן כל הקוד של ה

Template נמצא ב header file ,כפי שהוצג ,inline functionאו כ

ישנן לא רק מחלקות שהן גנריות אלא גם •פונקציות גנריות

28

פונקציות גנריות

int major (int a , int b , int c ) {

if (a == b || a == c) return a ;if (b == c) return b ;exit(1);

}: שימוש

int j = major (1,3,3); // ok?char c = major (‘w’,’w’,’w’);//ok?String s1 = major(s1,s2,s3); //ok ?int j = major (1,’a’,’a’); //ok ?

template <class T>T major (T a ,T b , T c ) {

if (a == b || a == c) return a ;if (b == c) return b ;exit(1);

}שימוש :

int j = major (1,3,3); // ok ?char c= major (‘w’,’w’,’w’); //ok?String s1 = major(s1,s2,s3); // ok?int j = major (1,’a’,’a’); //ok ?

29

Templates - advanced issues

לא “מתעניין” בתכונותיו Templateלמרות ש ה •יכול המיוחדות של הטיפוס המועבר כפרמטר הוא

ניח הנחות לגבי קיומם של פונקציות או מתודות להמסוימות בו.

? stack ? אילו T על majorאילו הנחות הניח –

ניתן להעביר כמה טיפוסים שונים בפרמטר:•template <class K, class D> class hashtable {

bool find (const K &k, D &d) ;…

};

30

Templateפרמטרים ל פרמטר שאינו טיפוס. פרמטר Templateניתן להעביר ל

יכול להיות מחרוזת, שם פונקציה או קבוע:template <class T, int Dim> class Vector {

T vec[Dim] ;…

};

Vector <int, 5> v ; Vector < double, 12 > v1; Vector <Complex, 3> v2; Vector <int , 6> v3 ;

הנו חלק מהטיפוס ? bufferמה היתרון בכך שגודל ה ? (v=v3)רמז מה יקרה ב

31

Templates - advanced issues

הינו טיפוס לכל דבר. אולם templateטיפוס שנוצר ע”י בדרך כלל כתיבת שמו המלא מסובכת לכן נעזרים

בקיצור הבא:typedef stack<int> stack_int ;

אם נרצה להגדיר מחסנית של מחסניות אזי נגדיר את הטיפוס

typedef stack<stack_int> stack_stack_int;

ונעזר בו:stack_int s1(100);

stack_stack_int s2(5);

s2.push(s1); // in this case push should have better // accepted const T& instead of T ...