1 / 28

Object Oriented Programming

Object Oriented Programming. Tirgul 12: Design Patterns Singleton Smart Pointer. Design Patterns Elements of Reusable Object-Oriented Software. ספרות. By: Erich Gamma,  Richard Helm,  Ralph Johnson, John Vlissides  http://st-www.cs.illinois.edu/users/patterns/dpbook/dpbook.html.

kura
Download Presentation

Object Oriented Programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Object Oriented Programming Tirgul 12: Design Patterns Singleton Smart Pointer

  2. Design PatternsElements of Reusable Object-Oriented Software ספרות By: Erich Gamma,  Richard Helm,  Ralph Johnson, John Vlissides  http://st-www.cs.illinois.edu/users/patterns/dpbook/dpbook.html

  3. מה זה design patterns? • אוסף של בעיות (תכנותיות) פופולריות והפיתרון להם • איננו תלוי שפה • אם כי, ישנן שפות אשר יתקשו במימוש נקי באי-אלו תבניות, או ביתר קלות בתבניות אחרות • ישנם הרבה מאוד design pattern (כ-30 תבניות פופולריות)

  4. משפחות של design patterns כמו בצבא, מתחלק ל-3: • Creational patterns • עוסק ב"איך ליצור אוביקט" מתאים • נלמד: Singleton Pattern • Structural patterns • עוסקות בהרכבה של אוביקטים • Behavioral patterns • תקשורת בין אוביקטים • למדנו:Iterator

  5. Singleton

  6. PrinterSingleton Printer Client Printer Client Printer Client

  7. למה צריך singleton? • כאשר אנו רוצים להבטיח כי ישנו רק מופע (instance) אחד (או, מקסימום N מופעים) של מחלקה • יש רק printer spooler אחד • ישנו file system אחד • ישנו רק windows manager אחד • לתת גישה גלובלית לאותה מחלקה • Printer Clients • נרצה ליצור לוגיקת singleton אצל האוביקט ה"מיוחד", כאשר המשתמשים אינם מודעים לכך

  8. אולי משתנה גלובלי/סטטי ? • משתנה גלובלי / סטטי. אומנם יתן גישה גלובלית אך: • זיהום מרחב השמות (הרבה משתנים גלובלים) • לא ימנע יצירת מופעים רבים (אפשר עדיין לעשות new) • יתכן ואין לנו את כל הנתונים לצורך יצירת מחלקה • ניצור מחלקה גם אם לא נשתמש בה • C++ לא מבטיחה סדר יצירה. לכן, singleton-ים שונים, אשר תלויים אחד בשני, לא יוכלו להיבנות (אין לנו שליטה על סדר היצירה). • נרצה ליצור לוגיקת singleton אצל האוביקט, כאשר המשתמשים אינם מודעים לכך

  9. אולי פונק' סטטיות ? • אם יש רק מופע אחד, למה לא לעשות את כל הפונק' במחלקה סטטיות? • אם בעתיד נרצה לשנות את כמות המופעים • שינוי קוד מסיבי, גם אצל המשתמשים • ב- C++ פונק' סטטיות הם אף פעם לא virtual • לא נוכל ליצור היררכיה (ירושה) של המחלקה

  10. אולי משתנה סטטי במחלקה ? • משתנה סטטי של המחלקה ייווצר עם השימוש הראשון במחלקה • יתכן שהשימוש יהיה ע"י קריאה לפונק' סטטית כלשהיא • סדר היצירה של משתנים סטטים איננו מוגדר:יתכן ומשתנה סטטי ירצה להשתמש ב-singleton עוד לפני יצירתו • נרצה lazy initilization: איתחול רק בזמן השימוש ראשון • לפני זה אין צורך • יתכן כי רק אז, יהיה לנו מספיק נתונים בשביל היצירה

  11. פתרון להסתיר את ה-:Constructor//PrinterSingleton.h class PrinterSingleton { public: static PrinterSingleton * GetInstance(); void Print(const char *); protected : PrinterSingleton (); // cannot invoke new from outside private: static PrinterSingleton * the_singleton; int m_iPagesPrinted; };

  12. // PrinterSingleton.cpp PrinterSingleton * PrinterSingleton::the_singleton = NULL; PrinterSingleton * PrinterSingleton::GetInstance() { if( NULL == the_singleton ) { the_singleton = new PrinterSingleton (); } return the_singleton; } PrinterSingleton:: PrinterSingleton ( ) { m_iPagesPrinted =0; } void PrinterSingleton:: Print (const char* sPrint) { // ... perform printing code m_iPagesPrinted++; cout << "Printing page number “ << m_iPagesPrinted << endl; cout << sPrint; }

  13. // main.cpp void main_func() { // … PrinterSingleton::GetInstance() ->Print("Print1 \n"); } int main() { main_func(); PrinterSingleton::GetInstance()->Print("Print2 \n"); return 0; } Output: Printing page number 1 Print1 Printing page number 2 Print2

  14. ומה עושים כאשר ל-singleton יש Dtor? • איך נקרא ל-D’tor של singleton בסוף התוכנית? • מאחר ואין שום משתנה אשר מחזיק את האובייקט אף אחד לא יקרא ל-D’tor • פתרון: ליצור משתנה סטטי אשר מחזיק את האובייקט

  15. public: //Inner Class class Keeper { public: Keeper(Logger* logger) : m_logger(logger) { } ~Keeper() { delete m_logger; } private: Logger* m_logger; }; friend class Logger::Keeper; }; // Logger.h class Logger { public: static Logger* GetInstance(); virtual bool write(string str); private: static Logger* the_singleton; fstream m_flog; protected: Logger(); // Ctor is protected virtual ~Logger();

  16. Logger::~Logger() { m_flog.close(); } bool Logger::write(string str) { m_flog.write(str.c_str(), str.length()); m_flog.flush(); return true; } // Logger.cpp Logger* Logger::the_singleton = NULL; Logger* Logger::GetInstance() { if (!the_singleton) the_singleton = new Logger(); return the_singleton; } Logger::Logger() { static Keeper keeper(this); m_flog.open("ourlog.out", ios::app | ios::out ); }

  17. // main.cpp int main() { Logger::GetInstance()->write("Log1\n"); // ... Logger::GetInstance()->write("Log2\n"); return 0; } Output (“ourlog.out”): Log1 Log2

  18. singleton במערכות עם כמה תהליכים במצב של ריבוי תהליכים (mutithreading) נצטרך להגן על GetInstance( ) מפני כניסה מקבילה Singleton* Singleton::GetInstance( ) { // lock – code to avoid concurrent activation if (!the_singleton) the_singleton = new Singleton(); // unlock – code to release locking return the_singleton; }

  19. Smart Pointer

  20. Smart Pointer – למה? • אובייקט שנראה ופועל כמו מצביע רגיל, אך נותן פונקציונאליות נוספת. • מנסים להשיג שתי מטרות עיקריות: • תכנות בטוח יותר • תכנות קל יותר. • ממומשים כמחלקות template אשר • מכמיסות את המצביע המובנה בשפה • עוקפות אופרטורים סטנדרטיים של המצביע.

  21. Smart Pointer – המשך ... • אחד השימושים הנפוצים במצביעים חכמים הוא לטובת Garbage Collection. • הרעיון: למנוע דליפות זיכרון • חשוב לזכור למחוק את המצביע לאובייקט. • קורה ששוכחים לקרוא במפורש ל- delete • ownership - במקרים מורכבים, המצביע מוחזר מפונקציה ולא ברור מי אחראי על שחרור המצביע • dangling pointer – השמה בטעות של המצביע במבנה נתונים, ואז, בטעות, מחיקת המצביע ה"מקורי"

  22. Smart Pointer – המשך ... • קיימות שפות בהן מנגנון ה- Garbage Collection הוא מובנה בשפה (למשל Java) • נגדיר מצביע חכם שהוא מחלקה ש: • מכילה את המצביע לאובייקט עצמו • מונה התייחסויות לאותו אובייקט. • כל פעם שנוצר מצביע חכם חדש לאובייקט - מגדילים את המונה באחד. • כשהמצביע החכם כבר לא מצביע לאובייקט אנחנו נפחית את המונה באחת. • כאשר המונה מתאפס, האובייקט נהרס.

  23. // SPtr.h: interface for the SPtr class. template <class T> class RefCnt { private: T* m_pObj; int m_cnt; public: RefCnt(T* pObj) : m_pObj(pObj), m_cnt(0) {} ~RefCnt() { assert(m_cnt==0); delete m_pObj; } T* operator*() {return m_pObj;} int inc() {m_cnt++; cout<<"increment to:"<<m_cnt<<endl; return m_cnt;} int dec() {m_cnt--; cout<<"decrement to:"<<m_cnt<<endl;return m_cnt;} };

  24. void operator=(const SPtr<T>& sp) { if(this!=&sp) { //first we need to delete the current // object, so we just decrement its // reference count and check if it's zero. if(m_ptr && (m_ptr->dec()==0)) delete m_ptr; m_ptr = sp.m_ptr; m_ptr->inc(); } } //we want to wrap the pointer to class T, // so we need to supply the -> operator T* operator->() { return (*m_ptr); } }; template <class T> class SPtr { private: RefCnt<T>* m_ptr; // hide new/delete operators to disallow // allocating a SPtr<T> from the heap. void* operator new(size_t) {} void operator delete(void*) {} public: SPtr() : m_ptr(NULL) { /* default Ctor */} SPtr(T* p) { m_ptr = new RefCnt<T>(p); m_ptr->inc(); } SPtr(const SPtr<T>& sp) { m_ptr = sp.m_ptr; m_ptr->inc(); } ~SPtr() { if(m_ptr->dec()==0) delete m_ptr; }

  25. class String { // String.h: implementation of string private: char* m_str; public: String(char* str) { m_str = new char[strlen(str)+1]; strcpy(m_str, str); } String(const String& str) { m_str = new char[strlen(str.m_str)+1]; strcpy(m_str, str.m_str); } ~String() { delete [] m_str; m_str=NULL; } }; typedef SPtr<String> SPtrString;

  26. String* GetTokenBad(istream& in) { char buffer[100]; in>>buffer; String* token = new String(buffer); return token; //who is suppose to delete this one??? }

  27. //converted to sptr: calls SPtr<String>(String*) with token. SPtrString GetTokenGood(istream& in) { char buffer[100]; in>>buffer; String* token = new String(buffer); return token; } void main() { // int i; // SPtr<int>* p = new SPtr<int>(&i); // ERROR: 'new' : cannot access private member declared in class 'SPtr<int>' SPtrString spStr; cout<<"Please enter a word: "; spStr = GetTokenGood(cin); } // call default Ctor Output: increment to:1 increment to:2 decrement to:1 decrement to:0 // call operator = // remove temporal object // end main: remove local variable: spStr

  28. אז מה עםgarbage collection ? • מדוע לא משתמשים ב-smart pointers כמימוש אוטומטי שלgarbage collection ? • מבני-נתונים מכילים מעגלים: • לכן צריך מימוש מתוחכם יותר

More Related