תרגול 12 Standard Template Library כתיבת אלגוריתמים גנריים מצביעים חכמים.

Slides:



Advertisements
Similar presentations
ממיבחניםC שאלות ++.
Advertisements

תוכנה 1 סמסטר א ' תשע " ב תרגול מס ' 7 * מנשקים, דיאגרמות וביטים * לא בהכרח בסדר הזה.
מבוא למדעי המחשב לתעשייה וניהול
מבוא למדעי המחשב לתעשייה וניהול דוגמאות ותרגול נוסף במערך חד ממדי הרצאה 12.
1 מבוא למדעי המחשב הקצאה דינאמית. 2 הקצאת זיכרון דינאמית  כאשר אנו משתמשים במערכים, אנו מקצים אוטומטית את הזיכרון המקסימלי שנצטרך.  בפועל, אנו משתמשים.
תכנות מונחה עצמים Object Oriented Programming (OOP) אתגר מחזור ב'
רקורסיות נושאי השיעור פתרון משוואות רקורסיביות שיטת ההצבה
הורשה ופולימורפיזם 1 עיגול ריבוע משושה צורה מוטיבציה מנשק גרפי טיפוסי מורכב מ -Widgets 2 Entry Label Button Check Button.
תכנות תרגול 11 שבוע : מבנים מטרת המבנים היא לאפשר למתכנת להגדיר טיפוסי משתנים חדשים אשר מתאימים ספציפית לבעיה שאותה התוכנית פותרת. מטרת המבנים.
תרגול 5 רקורסיות. רקורסיה קריאה של פונקציה לעצמה –באופן ישיר או באופן עקיף היתרון : תכנות של דברים מסובכים נעשה ברור ונוח יותר, מכיוון שזו למעשה צורת.
מה החומר למבחן ? כל החומר שנלמד בהרצאות ובתרגולים. לגבי backtracking: לא תידרשו לממש אלגוריתם, אך כן להבין או להשלים מימוש נתון. אחת משאלות המבחן מבוססת.
מבוא לשפת C חידות ונקודות חשובות נכתב על-ידי יורי פקלני. © כל הזכויות שמורות לטכניון – מכון טכנולוגי לישראל.
חורף - תשס " ג DBMS, צורות נורמליות 1 צורה נורמלית שלישית - 3NF הגדרה : תהי R סכמה רלציונית ותהי F קבוצת תלויות פונקציונליות מעל R. R היא ב -3NF.
11 Introduction to Programming in C - Fall 2010 – Erez Sharvit, Amir Menczel 1 Introduction to Programming in C תרגול
מבוא למדעי המחשב © אריק פרידמן 1 מצביעים כמערכים דוגמה.
תרגול חזרה. מבנה האובייקט תאר את מבנה האובייקט כולל מבנה טבלאות הפונקציות הוירטואליות עבור התכנית הבאה struct A { int x; virtual void a() {}; }; struct.
Formal Specifications for Complex Systems (236368) Tutorial #6 appendix Statecharts vs. Raphsody 7 (theory vs. practice)
תכנות תרגול 6 שבוע : תרגיל שורש של מספר מחושב לפי הסדרה הבאה : root 0 = 1 root n = root n-1 + a / root n-1 2 כאשר האיבר ה n של הסדרה הוא קירוב.
1 Formal Specifications for Complex Systems (236368) Tutorial #1 Course site : T.A. :Emilia Katz.
תכנות – שיעור 7. חזרה -מערכים נגדיר בעזרתו קבוצת משתנים כאשר יהיה לנו מספר רב של משתנים זהים נגדיר בעזרתו קבוצת משתנים כאשר יהיה לנו מספר רב של משתנים.
מערכות הפעלה ( אביב 2009) חגית עטיה ©1 מערכת קבצים log-structured  ה log הוא העותק היחיד של הנתונים  כאשר משנים בלוק (data, header) פשוט כותבים את הבלוק.
תכנות תרגול 10 שבוע : הקשר בין מערכים למצביעים נרצה לעמוד על הקשר בין מערך למצביע מאחר ומערכים הם הכללה של משתנים הרי שברור שלמערך ולכל אחד מאיבריו.
תכנות תרגול 14 שבוע : רשימות מקושרות ישנו מבנה נתונים אשר מאפשר ישנו מבנה נתונים אשר מאפשר לנו לבצע את הוספת האיברים בצורה נוחה יותר. מבנה זה.
מבוא כללי למדעי המחשב רשימות מקושרות
מערכים עד היום כדי לייצג 20 סטודנטים נאלצנו להגדיר עד היום כדי לייצג 20 סטודנטים נאלצנו להגדיר int grade1, grade2, …, grade20; int grade1, grade2, …, grade20;
עקרון ההכלה וההדחה.
תכנות מונחה עצמים Object Oriented Programming (OOP) אתגר מחזור ב' Templates תבניות.
תוכנה 1 - תרגול שיעור 10 Pointers (2) שולי לב יהודי
1 מבוא למדעי המחשב סיבוכיות. 2 סיבוכיות - מוטיבציה סידרת פיבונאצ'י: long fibonacci (int n) { if (n == 1 || n == 2) return 1; else return (fibonacci(n-1)
תכנות תרגול 12 שבוע : מבנים מטרת המבנים היא לאפשר למתכנת להגדיר טיפוסי משתנים חדשים אשר מתאימים ספציפית לבעיה שאותה התוכנית פותרת. מטרת המבנים.
מבוא למדעי המחשב תרגול 12 – הקצאת זיכרון דינאמית שעת קבלה : יום שני 11:00-12:00 דוא " ל :
11 Introduction to Programming in C - Fall 2010 – Erez Sharvit, Amir Menczel 1 Introduction to Programming in C תרגול
אתחול עצמים. אתחולים ובנאים יצירת מופע חדש של עצם כוללת: הקצאת זכרון, אתחול, הפעלת בנאים והשמה לשדות במסגרת ריצת הבנאי נקראים גם הבנאי/ם של מחלקת הבסיס.
Structure. מה לומדים היום ? דרך לבנות מבנה נתונים בסיסי – Structure מייצר " טיפוס " חדש מתאים כאשר רוצים לאגד כמה משתנים יחד דוגמאות : עובד : שם, טלפון,
הגדרת משתנים יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר 2015 יום שישי 18 ספטמבר.
פיתוח מערכות מידע Class diagrams Aggregation, Composition and Generalization.
Methods public class Demonstrate { public static void main (String argv[]) { public static void main (String argv[]) { int script = 6, acting = 9, directing.
1 מבוא למדעי המחשב הרצאה 21: Queue, Iterator & Iterable.
מבוא למדעי המחשב לתעשייה וניהול הרצאה 7. סברוטינות subroutines.
 Client, Supplier ומה שביניהם ( ADT!).  שאלה 1: יצירת ADT עבור מעגל במישור נניח שלקוח מעוניין בפעולות הבאות : הזזת מעגל וחישוב שטח מעגל. הספק יספק ללקוח.
Practice session 6 Sequence Operations Partial Evaluation Lazy Lists.
קורס תכנות שיעור עשירי: מיונים, חיפושים, וקצת סיבוכיות חישוב.
שיאון שחוריMilOSS-il מוטיבציה  python זה קל ו C זה מהיר. למה לא לשלב?  יש כבר קוד קיים ב C. אנחנו רוצים להשתמש בו, ולבסס מעליו קוד חדש ב python.
תכנות מכוון עצמים ושפת ++C וויסאם חלילי. TODAY TOPICS: 1. Function Overloading & Default Parameters 2. Arguments By Reference 3. Multiple #include’s 4.
מבוא למדעי המחשב לתעשייה וניהול הרצאה 12. ספריות.
מבנים קרן כליף. ביחידה זו נלמד :  מהו מבנה (struct)  איתחול מבנה  השמת מבנים  השוואת מבנים  העברת מבנה לפונקציה  מבנה בתוך מבנה  מערך של מבנים.
מבוא למדעי המחשב לתעשייה וניהול הרצאה 6. מפעל השעווה – לולאות  עד עכשיו  טיפלנו בייצור נרות מסוג אחד, במחיר אחיד  למדנו להתמודד עם טיפול במקרים שונים.
1 תרגול 11: Design Patterns ומחלקות פנימיות אסף זריצקי ומתי שמרת 1 תוכנה 1.
1 נתבונן בפונקציה הבאה public static int min(int[] a,int n) { int min = a[0]; for (int i = 1; (i < n ) && (i < a.length) ; i++) if (min > a[i]) min = a[i];
מחרוזות – הטיפוס String
מבני נתונים רשימה מקושרת, מחסנית ותור
Object Oriented Programming
Object Oriented Programming
Computer Architecture and Assembly Language
Operators Overloading
Object Oriented Programming
מחלקות classes.
מבוא למדעי המחשב סיבוכיות.
מצביעים קרן כליף.
תירגול 14: מבני נתונים דינאמיים
הרצאה 06 רשימות מקושרות קרן כליף.
ממשקים - interfaces איך לאפשר "הורשה מרובה".
הרצאה 21: Queue, Iterator & Iterable
ניתוח מערכות מידע תכנות ב C#
תוכנה 1 תרגול 13 – סיכום.
מחסנית ותור Stacks and Queues.
תירגול 8:מצביעים והקצאה דינאמית
שיעור עשירי: מיונים, חיפושים, וקצת סיבוכיות חישוב
Computer Programming תרגול 3 Summer 2016
Engineering Programming A
Presentation transcript:

תרגול 12 Standard Template Library כתיבת אלגוריתמים גנריים מצביעים חכמים

ספרית התבניות הסטנדרטית קיימת בכל מימוש של ++C מכילה אוספים (Containers) ואלגוריתמים. משתמשת בתבניות (templates): –אוספי הנתונים גנריים –האלגוריתמים המסופקים גנריים –מאפשרת הרחבה ע " י המשתמש –שומרת על ביצועים גבוהים 2

Vector נכיר תחילה את האוסף הפשוט ביותר בספריה – vector. –מערך סדור של איברים –מאפשר גישה לכל איבר באוסף –מאפשר הוספת והסרת איברים הערה : הקוד בשקפים הוא חלקי וחסרות בו תכונות נוספות ומתקדמות יותר של אוספי ה -STL. דוגמאות הקוד מתרכזות בשימוש בסיסי ופשוט. 3

מתודות נבחרות של vector template class vector { vector(); vector(const vector& c); vector(size_t num, const T& val = T()); ~vector(); T& operator[](size_t index); const T& operator[](size_t index) const; vector operator=(const vector& v); T& at(size_t loc); const T& at(size_t loc) const; void pop_back(); void push_back(const T& val); size_t size() const; }; הערה : גישה בעזרת אופרטור [] אינה בטוחה אינה מוודאת את חוקיות האינדקס לעומת זאת, גישה בעזרת at() זורקת חריגה במקרה והאינדקס אינו חוקי מסוג out_of_range 4

דוגמאות לשימוש ב -vector int main() { vector v1; // empty vector vector v2(20, 8);// 20 integers, all of value 8 for(size_t i = 0; i < v2.size(); ++i) { cout << v2[i]; // Unsafe access by index } for(int i = 0; i < 10; ++i) { v1.push_back(i); // Inserts items at the end of the vector } while(v1.size() > 0) { v1.pop_back(); } const vector copy(v1);//Safely copies a vector return 0; } 5

יתרונות ניהול זיכרון ע " י המחלקה –לא צריך לזכור לשחרר ידנית את המערך –ניתן לשנות את גודל המערך בקלות –ניתן ליצור העתקים ולבצע השמות בקלות גישה בטוחה –רק כאשר משתמשים ב -at() 6

שאלה : כיצד ניתן ליצור וקטור בטוח ? בד " כ מחיר בדיקת גישה לוקטור אינו משפיע על ביצועי התכנית. ניצור גרסה של וקטור המוודאת לכל גישה האם היא חוקית : template class SafeVector : public vector { SafeVector() : vector () {} SafeVector(int s) : vector (s) {} T& operator[](int i) { return at(i); } const T& operator[](int i) const { return at(i); } }; 7

List דומה לוקטור, אך המימוש הוא ברשימה מקושרת. –שימוש ברשימה מקושרת מאפשר הוספת איברים מהירה יותר, אך אינו מאפשר גישה מהירה לאמצע האוסף. –ההבדל העיקרי בין list ל -vector הוא במימוש, לכן לשני האוספים מנשק דומה. –השימוש במנשק דומה מאפשר למשתמש להחליף בקלות את סוג האוסף בשימוש גם בשלבים מאוחרים.  כאמור, סיבוכיות היא שיקול משני בד " כ ולכן ניתן להשתמש ב -vector, ורק בהמשך כשיש צורך ברשימה מקושרת ניתן לעבור לשימוש בה. 8

מתודות נבחרות מ -list template class list { list(); list(const list& c); list(size_t num, const T& val = T()); ~list(); list operator=(const list& v); void pop_back(); void push_back(const T& val); void pop_front(); void push_front(const T& val); size_t size() const; }; נוספו מתודות להסרת והוספת איברים גם בראש הרשימה נעלמו המתודות לגישה לאיבר באמצע הרשימה כיצד נוכל לאפשר גישה למשתמש בצורה נוחה ? 9

Iterators ע " מ לאפשר מעבר סדור על איברי אוסף נשתמש בעצם מסוג Iterator. נדרוש את הפעולות הבאות מ -Iterator ( בינתיים ): –קידום – שינוי האיטרטור כך שיצביע לאיבר הבא –קריאה – החזרת האיבר המוצבע ע " י האיטרטור –השוואה – בדיקה האם שני איטרטורים מצביעים לאותו איבר איטרטור אינו מחלקה, אלא מושג (concept). למשל מצביע הוא איטרטור עבור מערך של C: int* array = new int[n];... int* i = array; cout << *i << endl; // Reading the iterator i++; // Advancing the iterator by 1 if (i == array+n) { // Comparing to iterators cout << "End of array!" << endl; } 10

Iterators & STL כל האוספים המאפשרים מעבר על איבריהם ב -STL מספקים iterator בעזרתו ניתן לעבור על איבריהם. המתודה begin() מחזירה iterator מתאים לתחילת האוסף. המתודה end() מחזירה iterator מתאים לאיבר " דמה " אחרי האיבר האחרון. למשתמש לא אכפת כיצד ממומש האיטרטור ! 11

דוגמה לשימוש בוקטור הדפסת איברי vector: for(vector ::iterator i = v.begin();i != v.end(); ++i) { cout << *i << endl; } הדפסת איברי list: for(list ::iterator i = l.begin(); i != l.end(); ++i) { cout << *i << endl; } בצורה דומה ניתן לגשת לכל אוסף ב -STL הטיפוס עבור ה -iterator מוגדר בתוך כל אוסף בצורה הבאה : container ::iterator 12

const_iterator בנוסף כל אוסף מגדיר גם טיפוס const_iterator. –כך ניתן להגן על האוסף מפני שינויים כאשר הוא מוגדר כ -const –טיפוס האיטרטור המוחזר נקבע בעזרת העמסה על המתודות המתאימות vector v; vector ::iterator i = v.begin(); *i = 7; // O.K.!! // *i is int& const vector v; vector ::const_iterator i = v.begin(); *i = 7; // syntax error! // *i is const int& 13

עוד שימושים רוב המתודות עבור האוספים משתמשות באיטרטורים כדי לציין מיקומים. –הוספת איבר לתוך רשימה ממוינת : list ::iterator i = myList.begin(); while (i != myList.end() && num < *i){ ++i; } myList.insert(i, num); // Insert num before I –מחיקת האיבר החמישי בוקטור : myVector.erase(v.begin()+4); 14

סוגי איטרטורים אוספים שונים מחזירים איטרטורים שונים. ל -list, למשל, יש bidirectional iterator המאפשר קידום האיטרטור עם אופרטור ++ והחזרתו לאחור עם אופרטור --. ל -vector יש random access iterator המאפשר גם חיבור מספרים לאיטרטור וחישוב הפרש בין שני איטרטורים ( בדומה למצביע ). 15

עוד STL? ל -vector ו -list מס ' רב של מתודות ואופרטורים נוספים. –כמעט כל פעולה שעולה בדעתכם לבצע כבר ממומשת ב -STL. –ניתן פשוט לחפש באינטרנט :  למשל יש עוד אוספים ב -STL. –למשל set, stack ומבני נתונים נוספים שלא נלמדו בקורס. 16

כתיבת אלגוריתמים גנריים ברצוננו לכתוב פונקציה אשר מוצאת את האיבר המקסימלי. כיצד נוכל לאפשר לפונקציה לעבוד על כל אוסף אפשרי ? 17

אלגוריתם max template Iterator max(Iterator start, Iterator end) { if (start == end) { return end; } Iterator maximum = start; for(Iterator i = ++start; i != end; ++i) { if (*i > *maximum) { maximum = i; } return maximum; } 18

אלגוריתם max - שימושים דוגמאות לשימוש : –מציאת הערך הגדול ביותר בוקטור. int m = *max(myVector.begin(), myVector.end()); – מחיקת האיבר הגדול ביותר ברשימה. myList.erase(max(myList.begin(), myList.end())); 19

יתרונות אלגוריתם יחיד מתאים לכל המקרים : –אוספים שונים –רק חלקים מהאוסף ניתנים למעבר –קל להתאים קוד קיים שיעבוד עם האלגוריתם  למשל שימוש באלגוריתם עבור מערך רגיל של C: int* myArray = new int[size];... int m = *max(myArray, myArray+size); 20

algorithm מלבד מבני הנתונים ה -STL מכיל גם אוסף אלגוריתמים מועילים הניתנים להפעלה על כל מבנה נתונים מתאים : חלקם פשוטים : Iterator find(Iterator start, Iterator end, const T& val); וחלקם פחות : void sort(Iterator start, Iterator end); void random_shuffle(Iterator start, Iterator end); 21

Function Objects במקום לשלוח מצביעים של פונקציות ב -STL משתמשים ב -“Function objects”. Function object הוא כל עצם המעמיס את אופרטור () ( אופרטור ההפעלה ) 22

דוגמה ל -Function Object class LessThanZero { public: bool operator()(int m) const { return m < 0; } }; int main() { LessThanZero isNegative; cout << isNegative(-4) << endl; // true cout << isNegative(6) << endl; // false return 0; } את המחלקה מגדירים עם אופרטור ההפעלה. במקרה זה אנו מגדירים את המחלקה כפונקציה המקבלת int ומחזירה bool. 23

דוגמה ל -Function Object class SumOf { public: bool operator()(int a, int b, int s) { return a+b == s; } }; int main() { SumOf isSum; cout << isSum(2,3,5) << endl; // true cout << isSum(1,2,5) << endl; // false return 0; } אופרטור ההפעלה ניתן להגדרה עם כל מספר של פרמטרים. 24

דוגמה ל -Function Object class LessThan { public: LessThan(int n) : n(n) {} bool operator()(int m) const{ return m < n; } private: int n; }; int main() { LessThan isLess(5); LessThan isLess2(8); cout << isLess(4) << endl; // true cout << isLess(6) << endl; // false cout << isLess2(6) << endl; // true return 0; } עד עכשיו לא הצגנו את היתרון על שימוש במצביע לפונקציה : בניגוד למצביעים לפונקציות, ניתן לזכור מצב ב -Function Object ולכן ניתן להשתמש באותה מחלקה למס ' פונקציות השונות רק בפרמטר כלשהו. ( היזכרו כיצד פתרנו את אותה בעיה ב -C) שימו לב להבדל בין קריאה לבנאי להפעלת האופרטור המתבצעת על עצם מהטיפוס. 25

Function Objects - דוגמה נוכל להשתמש בפונקציה שהגדרנו בקריאה לאלגוריתמים מסוימים : template Iterator find_if(Iterator first, Iterator last, Predicate pred) { for (; first != last; first++) if (pred(*first)) break; return first; } vector v; vector ::iterator i = find_if(v.begin(),v.end(),LessThan(2)); vector ::iterator j = find_if(v.begin(),v.end(),LessThan(7)); 26

דוגמה נוספת ל -Function Objects ניצור קריטריון מיון חדש למספרים שלמים, נמיין לפי ערך המספר מודולו m: class LessThanModulo { public: LessThanModulo(int m) : m(m) {} bool operator()(int a, int b) {return a%m < b%m;} private: int m; }; בכדי למיין פשוט נקרא לאלגוריתם המיון עם פרמטר נוסף : sort(v.begin(), v.end(), LessThanModulo(5)); 27

הוספת איטרטורים ניזכר במחלקת String שהגדרנו בתרגול 9, כיצד נוכל להשתמש באלגוריתמים הקיימים עבור מחלקה זו ? עלינו להוסיף תמיכה באיטרטורים במחלקה. 28

הוספת תמיכה באיטרטורים ב -String class String { private: int length; char* value; public:... typedef char* iterator; typedef const char* const_iterator; iterator begin() { return value; } const_iterator begin() const { return value; } iterator end() { return value+length; } const_iterator end() const { return value+length; }... }; במקרה של String, נוכל פשוט להשתמש במצביע ל -char. שימו לב להגדרות הטיפוסים שאנו מוסיפים, כדי שהמשתמש לא יצטרך לדעת בעצמו שאנו משתמשים במצביע ל -char 29

שימוש האלגוריתם transform מפעיל פונקציה על תחום מסוים ומציב לתחום אחר את ערך ההחזרה. נשתמש בו ובפונקצית הספריה של C toupper() כדי להמיר מחרוזת לאותיות גדולות. transform(str.begin(),str.end(),str.begin(),toupper); 30

סיכום הספריה הסטנדרטית בנויה מאוספים ואלגוריתמים איטרטורים מהווים את הדבק המאפשר לשני חלקים אלו להישאר גנריים אוספיםאוספיםאלגוריתמיםאלגוריתמים איטרטוריםאיטרטורים 31

מצביעים חכמים מצביעים אחראים לרובן המוחלט של שגיאות הזיכרון הצלחנו להיפטר מרוב השימושים הלא בטוחים במצביעים בעזרת שימוש בתכונות של ++C –ניצול בנאים, הורסים והשמות לטיפול מסודר בזיכרון. –שימוש ב -STL ואוספים לטיפול מסודר באלגוריתמים של מבני הנתונים. 32

בעיה vector shapes; Circle circle(3.5); shapes.push_back(circle); // Only the "Shape part" // is copied!! vector shapes;... delete shapes.back(); shapes.pop_back(); // We must delete ourselves // when removing items from // the vector ירושות דורשות שימוש במצביעים עבודה עם מצביעים מוחקת את כל היתרונות שהשגנו ! –צריך לנהל זיכרון בצורה מפורשת בכל מקום בתוכנית ! 33

smart_ptr נוכל ליצור מחלקה, המתנהגת כמצביע אך מוסיפה התנהגות נוספת. –למשל, שחרור העצם המוצבע בהריסת המצביע. כלומר ניצור מצביע " חכם " יותר. 34

smart_ptr template class smart_ptr { private: T* pointedData; typedef T element_type; public: explicit smart_ptr(T* ptr = NULL) : pointedData(ptr) {} ~smart_ptr() { delete pointedData; } T& operator*() const { return *pointedData; } T* operator->() const { return pointedData; } }; 35

יתרונות שימוש ב -smart_ptr מה קורה במקרה וה -new השני נכשל בכל אחת מהפונקציות ? int bad() { Shape* ptr1 = new Circle(5.0); Shape* ptr2 = new Square(5.0);... } int good() { smart_ptr ptr1(new Circle(5.0)); smart_ptr ptr2(new Square(5.0));... } 36

העתקה כיצד יתנהג המצביע במקרה של העתקה ? –מה צריכה להיות ההתנהגות עבור בנאי העתקה והשמה ? ניסיון ראשון : העתקת המצביע תבצע " העברת בעלות " של העצם המוצבע –התנהגות זו ממומשת ב -auto_ptr הקיים בספריה הסטנדרטית של ++C. –מאפשרת העברת / החזרת עצמים מוקצים דינאמית מפונקציות. 37

שימוש ב -auto_ptr שימוש ב -auto_ptr כדי להחזיר עצם חדש : –בניגוד למצביע רגיל – מוגדר בקוד מי אחראי לשחרור העצם. auto_ptr createMyShape() { return auto_ptr (new Circle(5.0)); } שימוש ב -auto_ptr כדי לחסוך העתקות : auto_ptr > getNPrimeNumbers (int n) {... } 38

העתקה בעיה ! ההתנהגות עבור " העתקה " של auto_ptr אינה סטנדרטית –ההעתק אינו זהה למקור !  לא ניתן לאחסן auto_ptr בתוך אוספים של ה -STL מאחר והם מסתמכים על תכונה זו. כיצד ניתן ליצור מצביע חכם שגם יאפשר העתקה נכונה ? 39

שיפור – מצביע משותף ניצור מצביע חכם יותר מתקדם : shared_ptr לכל עצם מוצבע נשמור מונה הצבעות, וכל המצביעים ישתמשו במונה זה על מנת לדעת מתי יש לשחרר את העצם המוצבע. Object Object* ptr int* counter shared_ptr Object* ptr int* counter shared_ptr Object* ptr int* counter shared_ptr 40

משתנים ואינווריאנטה template class shared_ptr { private: T* pointedData; int* referenceCounter; typedef T element_type; void checkInvariant() const { assert((pointedData && referenceCounter && (*referenceCounter)>0) || (!pointedData && !referenceCounter)); } 41

עדכון המונה void increaseCount() { checkInvariant(); if (referenceCounter) { (*referenceCounter)++; } void decreaseCount() { checkInvariant(); if(referenceCounter && --*referenceCounter == 0){ delete referenceCounter; delete pointedData; referenceCounter = NULL; pointedData = NULL; } 42

אתחול ושחרור public: explicit shared_ptr(T* ptr = NULL) : pointedData(ptr), referenceCounter(pointedData ? new int(1) : NULL){} shared_ptr(const shared_ptr & other) : pointedData(other.pointedData), referenceCounter(pointedData ? other.referenceCounter : NULL) { increaseCount(); checkInvariant(); } 43

אתחול ושחרור template friend class shared_ptr; template shared_ptr(const shared_ptr & other) : pointedData(other.pointedData), referenceCounter(pointedData ? other.referenceCounter : NULL) { increaseCount(); checkInvariant(); } ~shared_ptr() { checkInvariant(); decreaseCount(); checkInvariant(); } מטרת הבנאי היא לאפשר השמה בין shared_ptr של טיפוסים שונים, למשל בגלל הורשה. 44

גישה למצביע T& operator*() const { checkInvariant(); assert(pointedData != NULL); return *pointedData; } T* operator->() const { checkInvariant(); assert(pointedData != NULL); return pointedData; } T* get() const { checkInvariant(); return pointedData; } המתודה get() מסופקת בכדי לאפשר שימוש של המחלקה גם עם קוד ישן שאינו תומך ב -shared_ptr 45

אופרטור השמה template shared_ptr & operator=( shared_ptr & other) { checkInvariant(); other.increaseCount(); decreaseCount(); referenceCounter = other.referenceCounter; pointedData = other.pointedData; checkInvariant(); return *this; } מה קורה במקרה של הצבה עצמית ? מדוע אין צורך בבדיקה ? 46

operator bool() { checkInvariant(); return pointedData; } void reset() { decreaseCount(); pointedData = NULL; referenceCounter = NULL; } }; המרה לערך בוליאני תאפשר לנו לבדוק האם המצביע תקין כמו מצביע רגיל. המתודה reset() מאפשרת למשתמש צורה נוחה יותר לאיפוס מצביע עוד מתודות שימושיות 47

פונקציות השוואה template bool operator==(const shared_ptr & first, const shared_ptr & second) { return first.get() == second.get(); } template bool operator!=(const shared_ptr & first, const shared_ptr & second) { return !(first==second); } template bool operator & first, const shared_ptr & second) { return first.get() < second.get(); } 48

דוגמאות vector > function() { shared_ptr shape(new Polygon(points)); shared_ptr circle(new Circle(5.0)); shared_ptr square(new Square(2.0)); vector > vec; vec.push_back(shape); vec.push_back(circle); vec.push_back(square); vector > copy = vec; copy.pop_back(); return copy; } כל ההעתקות בטוחות ! האחרון שיוצא סוגר את האור ! תרגיל לבית ( למשועממים ): מיצאו את הצורה שיש למחוק בסוף הפונקציה... איך היה נראה הקוד ללא שימוש במצביעים חכמים ? 49

יתרונות כל הקוד לניהול הזיכרון נכתב רק פעם אחת. קל לחלוק עצמים אשר לא ברור מי האחרון שמשתמש בהם. ניתן להשתמש בכל האוספים שראינו עם מצביעים ובפרט עם ירושות. הקוד נהייה פשוט יותר ועמיד יותר בפני שגיאות זיכרון. –מה קורה בדוגמה הקודמת אם אחת מהקצאות הזיכרון נכשלת ? מה היה קורה ללא המצביעים החכמים ? 50

לא הכל מושלם ! shared_ptr אינו מתאים לאחסון מצביע למערך ! –כי השחרור הוא ע " י delete ולא delete[]. –ניתן ליצור בקלות shared_array שיתאים לבעיה זו.  אבל ממילא עדיף להשתמש ב -vector! המצביעים לא ישוחררו אם קיימים קשרים מעגליים –במקרה זה יש לנקות את המצביעים בצורה מפורשת עם reset. –פתרון טוב יותר למקרה זה דורש היכרות עם מצביע חכם נוסף והזמן קצר מלהכיל אותו. 51

לסיכום כתיבת ספריות ב -++C היא מסובכת. השימוש בהן לעומת זאת הוא פשוט. –מאפשר כתיבת קוד טוב יותר בפחות זמן ופחות שורות. ניתן למצוא מימוש פשוט של shared_ptr באתר הקורס. 52