1 / 38

Copying, casting, and more

Copying, casting, and more. Example: MyString. בג'אבה שהיא שפת רפרנסים זה בעיה להעביר למתודה אובייקט שהוא העתק ולא מצביע למקור (צריך לעשות קְלוֹן וכדו') Lets put our knowledge of C++ classes to use Define a class to represent a string

tirzah
Télécharger la présentation

Copying, casting, and more

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. Copying, casting, and more

  2. Example: MyString • בג'אבה שהיא שפת רפרנסים זה בעיה להעביר למתודה אובייקט שהוא העתק ולא מצביע למקור (צריך לעשות קְלוֹן וכדו') • Lets put our knowledge of C++ classes to use • Define a class to represent a string • Replace all the calls to strdup, strcmp, … with methods that are clearer and handle memory allocation • [See MyString.h MyString.cpp]

  3. Are we done yet? • Using MyString we can now write code • יש קוד שצריך להסתכל עליו פה (לא יודעת איזה, יש באתר...) For example MyString str1(“Foo”); MyString str2(“Bar”); … if( str1 > str2 ) str1 = str2; // Whoa! what does this do? … נרצה שאותן הפעולות שמוגדרות על אובייקטים פרימיטיביים (אינט, צ'אר וכדו) יפעלו גם על אובייקטים מסובכים.

  4. Few Words on Copy… What does the assignment str1 = str2 do? High-level view: • “=“ is an operator (like “+”)מקבל פרמטר אחד אחרי השווה, ומפעיל על האובייקט השמאלי • The commend specified here can be written str1.operator=(str2) • The compiler searches for a myString method function (operator =) with argument of type MyString Function name Argument

  5. operator = • For built-in types, the language behaves as though there are functions: int& operator=(int&, const int &);למה הוא מקבל שני פרמטרים? כי אינט הוא פרימיטיבי, ולכן נבנתה עבורו מתודת אופרטור= משלו, שמקבלת שני ארגומנטים, משנה את השמאלי מביניהם על פי הימני, ומחזיר רפרנס לאינט הראשון, לאחר ששונה. למה רפרנס? בכל העברה לפונקציה נעביר רפרנס כדי לחזוך בזכרון, בזמן ריצה, ומיד נראה סיבה נוספת double& operator=(double&, const double &); … This is why we can write: int a, b; (a = b = 5)++; // equivalent to: b = 5; a = b; a++;

  6. What about classes? • The same operator for a class X would have the type signature X& X::operator=(const X & ); כאשר עושים אוברלוד למתודה שקיימת, כמו כאן, צריך שהמטרה והאלגוריתם יהיו זהים למטרה והאלגוריתם המקוריים שיועדו, על מנת לא ליצור סתירה בציפייה מתוצאות המתודה Why • const X & argument? • To ensure that the right-hand side of copy does not change (why ref? later on) • The X& return type? • To allow for x = y = z like built-in types

  7. MyString Revisited • We never defined MyString::operator= • Why our example compiles? • The compiler defines default instantiation of operator= • למה? כדי שתהיה התאמה בין סי לבין סיפיפי • This happens for every class for which the programmer did not define his own operator= • Saves unneeded work for many classes

  8. Default operator= • The default operator= copies all data members of the class • In our case, the result is MyString& MyString::operator=(const MyString &rhs) { _length = rhs._length; _string = rhs._string; return *this; // return reference to // current object }

  9. str1: str2: _string _string Example Revisited void boringExample() { MyString str1(“Foo”); MyString str2(“Bar”); if( str1 > str2 ) str1 = str2; } Problem! • A memory location is deleted twiceפעם אם היו מוחקים פעמיים את אותו האובייקט, זה לא היה עושה בעיה, אבל עכשיו זה כן עושה בעיה. • Memory leak str1 constructor str2 constructor str1.operator=(str2) str1 destructor str2 destructor “Foo” “Bar”

  10. The fix? • Define our own operator= MyString& MyString::operator=(const MyString &rhs) { delete [] _string;למה מערך?כי סטרינג הוא מערך של קארים, ולכן צריך להודיע לדסטרקטור למחוק את כל אברי המערך – את כל אותיות הסטרינג m_length = rhs._length; init( rhs. _string ); return *this; } זה לא מושלם... פרטים בשקפים הבאים

  11. Is This Solution Water Tight? • What if a programmer writes MyString str(“foo”); … str = str; // senseless, but legal! What happens? • delete str._string • allocate a new str._string • copy this string onto itself… בעיה!! פתרון למקרה קצה זה – בשקף הבא.

  12. Checking for Self-copy • Add another check at the beginning of the procedure MyString& MyString::operator=(const MyString &rhs) { if( this == &rhs ) return *this; … אבל זה עדיין לא מושלם...

  13. Are We Out of The Woods? Consider the following code void doNothing( MyString S ) { } void anotherBoringExample() { MyString str(“foo”); doNothing(str); } What is wrong with this picture?

  14. Are We Out of The Woods? Consider the following code void doNothing( MyString S ) // MyString ctor called for S { } // MyString dtor called for S יש לשני הסטרינגים זיכרון משותף (עפ"י הקופי הדיפולטיבי) ולכן הדיסטרקטור שנקרא בסוף הפונקציה הזו הורס את שני הסטרינגים.מה נעשה? נבנה קופי-קונסטרקטור! void anotherBoringExample() { MyString str(“foo”); doNothing(str); } // MyString dtor called for str What is wrong with this picture? זכרון משוחרר פעמיים – תקלה ברמת האוגדה. S. _string “foo” Str. _string

  15. Copy Constructor What happens when we call DoNothing(str) ? • A new MyString object is created on the stack • This object needs to be constructed • It also needs to copy the value of str Copy constructor: MyString::MyString(const MyString &init);למה רפרנס? חוסך זמן. מעבר לזה, אם לא, תתקיים קריאה רקורסיבית ולולאה אין סופית

  16. Copy Constructor • This constructor is also called when initializing a new object MyString str2 = str1; is equivalent to writing MyString str2(str1); • In both cases the compiler calls copy constructor

  17. Default Copy Construct • Same as with operator= • If not specified, default instantiation is by copying all data members • Constructing each one with the value of the matching field in source In our example: MyString::MyString(const MyString &rhs) : _length( rhs. _length ), _string( rhs. _string ) {}

  18. Example Revisited void doNothing( MyString S ) { } void anotherBoringExample() { MyString str(“foo”); doNothing(str); } Problem! • str. _string is deleted by the destructor

  19. Fix? MyString::MyString(const MyString &rhs) { _length = rhs._length; init( rhs._string );מקצה זיכרון ומכניס את בסטרינג לשדה המתאים }

  20. Lessons If a class needs deep copy (for example, manages memory) , then • Define your own operator= and copy constructor • Remember to copy all data members • Make sure to check for self-copy • Remember that operator= returns a reference to the object • Define a destructor

  21. Disallowing Copy • Some times we want to create classes in which object cannot be copied • Large objects, such as a database • “One time” data structure • … • How do we ensure that object from this class are not copied?

  22. Disallowing Copy Solution #1: • Do not define operator= and copy constructor Problem: • Compiler will define default versions • This is not what we want…

  23. Disallowing Copy Solution #2: generate runtime error X& operator=(const X& x ) { assert(false); return *this; } Caveat: (בעברית – אזהרה) • Problems shows up very late in the development process…

  24. Disallowing Copy Solution #3: • Define the operator= and copy ctor as private class X { … private: X& operator=(const X&); X& X(const X&); }; • Cannot be called (compilation error) from outside methods of X

  25. class Base { … private: double _x; int _a; }; class Derived : public Base { … private: double _z; }; Base a; Derived b; … a = b;קורא להעתקה של בייס כי איי הוא בייס, לכן רק השדות של בייס יועתקו לאיי. אבל מה עוד יועתק מבי לאיי? הוירטואל טייבל של בי, שהיא מסוג דרייבד, תועבר להיות הוירטואל טייבל של איי. אוי ווי! אבל זה לא באמת קורה- פרטים מיד default copy will _x _a a: b: _z _x _a Copy & Inheritance

  26. f1 f2 b: <vtbl> f1 _a f2 f3 _b VTBLs A a1: <vtbl> _a B Copy & Inheritance class A { public: virtual void f1(); virtual void f2(); int _a; }; class B: public A { public: virtual void f1(); virtual void f3(); void f4(); int _b; }; … A a; B b; a = b;

  27. Copy & Inheritance – works well class A { public: int _a; A () : _a (5) {} }; class B: public A { public: int _b; B () : _b (3) {} }; int main () { B b;קודם כל נקרא הקונסטרקטור של איי, אחר כך של בי. b._a = 2; B c = b; // copy ctor std::cout << c._a << std::endl; } // output 2

  28. But:אוי לא! זה לא מה שציפינו! class A { public: int _a; A () : _a (5) {} A (const A &src) : _a (src._a) {} }; class B: public A { public: int _b; B () : _b (3) {} B (const B &src) : _b (src._b) {} }; int main () { B b; b._a = 2; B c = b; // copy ctorיש קונסטרקטור שהוא לא דיפולטיבי, ולכן במקום שייקרא קונסטרקטור האב (איי) לפני, כמו שהיה קורא במצב דיפולטיבי, הגדרת הקופי קונסטרקטור לא קוראת לו, ולכן היא עושה רק את בי. מה נעשה? בשקף הבא. std::cout << c._a << std::endl; } // output 5

  29. Don’t forget the base class A { public: int _a; A () : _a (5) {} A (const A &src) : _a (src._a) {} }; class B: public A { public: int _b; B () : _b (3) {} B (const B &src) : A (src), _b (src._b) {} }; int main () { B b; b._a = 2; B c = b; //copy ctor. std::cout << c._a << std::endl; } // output 2

  30. Function Resolution • In general, upon seeing the statement foo(x); The compiler searches for • a function foo with argument types that match x or • a function foo with argument types that can be casted to from the type of x

  31. Function Resolution • How does C++ determine whether it can cast type A to type B ? Standard Casts: • int  double, char  int, etc. • Upcasting Tailored Casts: • casts introduced by the programmer • למשל סטרינג של סי פיפי – פונקציה שמועבר אליה סטרינג יכולה לקבל גם מערך של קארים אם מגדירים את הקונסטרקטורים בצורה מתאימה. דוגמה מיד.

  32. Upcasting f(Base *b) { ...} Derived *d = ...; f(d);

  33. Tailored Casts - example class MyClass { public: MyClass(int i);המשמעות היא שניתן לתרגם כל אינט לאובייקט מסוג מיי-קלאס. … }; • The constructor is viewed as a way of taking an integer an making it into a MyClass object

  34. Tailored Casts - example void foo( MyClass x ); … int i; … foo(i); // What happens here? The compiled code for the last line is eq. to { MyClass tmp(i); foo(tmp); } מה קורה אם יש קונס' שמקבל כמה ארגומנטים, ומעבירים את אותם הארגומנטים לפונק'? זה לא כזה פשוט, כאן אנחנו מתעסקים בקונס' עם פרמטר 1, ופונק' כנ"ל

  35. Implicit Cast • This feature is useful for seamless operations • For example, MyString has a cast from char* • We can use “<String>” in calling functions that expect MyString MyString str; … if( str == “foo” ) … בדוגמה הזו ניתן להבחין בשתי משמעויות לאופרטור == - אולי זה הדיפולטיבי, ואולי זה אחד שאנחנו הגדרנו. NyString::operator==(const MyString)היינו מגדירים: אבל דיפולטיבי היה עושה את זה עם צ'אר*. למזלנו הקונס' של מיי סטרינג מקבל ארגומנט של צ'אר* ולכן יכולה להיות התאמה בכל מקרה. הערה: אם היינו מחליפים בין המיקומים שלפני ואחרי ה== בדוגמא הזו, זה לא היה מתקמפל... או משהו כזה...

  36. Implicit Cast class IntArray { public: IntArray(int size); // constructor bool operator==(const IntArray &rhs) const; int operator[](int i) const; … }; … IntArray A(5), B(5); … for( i = 0; i < 5; i++ ) if( A == B[i] ) // oops! should have been A[i] == B[i] … זו תקלה שתתגלה רק בזמן ריצה, ולכן נרצה למנוע זאת... This creates a new temporary IntArray of size B[i] !!!

  37. Explicit Keyword • A directive to compiler not to use constructor in implicit casts class IntArray { public: explicit IntArray(int size); // constructor … };

  38. Lessons • C++ can create temporary objects “behinds the scenes” To avoid this: • Use explicit keyword for constructors • Pass objects by reference void foo(const MyClass &Obj ); instead of void foo( MyClass Obj ); • Declare a private copy constructor

More Related