עקרונות תכנות מונחה עצמים תרגול 11: OOP in C++
Outline Where do the objects live ? Inheritance Slicing Overriding vs Shadowing
History
Memory segmant לכל תוכנית שרצה במחשב ( בין אם נכתבה בג ' אווה, ב ++C, או בכל שפה אחרת ) מוקצה מרחב זכרון עבור קוד התוכנית והמידע שלה. עבור משתני התכנית מוקצים שני מקטעים עיקריים – Stack & Heap כל אחד משני המקטעים שומר את המשתנים בדרך שונה ובעל ייעוד משלו
The Stack המחסנית משמשת לאחסון משתנים מקומיים ופרמטרים של פונקציה. לכל פונקציה נפתח פריים משלה במחסנית, בו נשמר כל המידע המקומי שלה. בחזרה מהפונקציה, הפריים שלה במחסנית נסגר ( וכל המידע המקומי נעלם ) וחוזרים לפריים של הפונקציה שקראה לה ( שנמצא מתחת במחסנית )
The Heap משמש לאחסון משתנים גם מעבר לסקופ של פונקציה בודדת. משמש להקצאה דינאמית של משתנים. בניגוד למחסנית, ב heap המשתנים לא מסודרים בהכרח ברצף, על פי סדר הקצאתם.
בג ' אווה כל המשתנים הפרימיטבים (int,double,char..) מוקצים על המחסנית, וכל האובייקטים מוקצים על ה heap ( כאשר במחסנית נשמר ' מצביע ' לכתובת שלהם ב heap) ב ++C, יש למתכנת יותר גמישות – גם משתנים פרימטיבים, וגם אובייקטים אפשר להקצות בכל אחד משני האזורים. Where do the objects live?
Java public class Point{ protected int _x; protected int _y; public Point( int x, int y){ this._x = x; this._y = y; } Where do the objects live? public class Point3D extends Point{ private int _z; public Point3D( int x, int y, int z){ super(x,y); this_z = z; }
C++ class Point{ protected: int _x; int _y; public: Point( int x, int y): _x(x), _y(Y){ } }; Where do the objects live? class Point3D : public Point{ private: int _z; public: Point3D( int x, int y, int z): Point(x,y){ this->_z = z; } };
Variables in Java JAVA: public static void main(String[] args){ int x = 5; double y; char c = ‘A’; Point p1= new Point(1,1); Point3D p2= new Point3D(2,2,2); Point p3= new Point3D(1,2,3); }
Java variables 0x ‘A’ 0x1000 0x2000 0x3000 x y c p1 p2 p x1000 0x2000 0x3000
Variables in C++ C++: void main(){ int x = 5; double y; char c(‘A’); Point *p1 = new Point(1,1); Point3D p2 (2,2,2); Point *p3 = new Point3D(1,2,3); }
C++ variables 5 ? ‘A’ 0x1000 0x3000 x y c p1 p2 p x1000 0x
Memory management משתנים המוקצים על המחסנית, נמחקים ממנה ביציאה מהפונקציה בהם הוקצו. משתנים המוקצים על ה Heap יש למחוק במפורש. בג ' אווה זה מתבצע על ידי ה Garbage Collector ב ++C זה באחריות המתכנת : void main(){ Point *p1 = new Point(1,1); Point3D p2 (2,2,2); Point *p3 = new Point3D(1,2,3); delete p1; delete p3; }
Slicing ראינו שבשתי השפות, אפשר לבצע את הפעולות הבאות : Point3D p3d = new Point3D(1,2,3); Point p = p3d ; זה מתאפשר כיוון שלא משנה אם האובייקט ב heap הוא Point או Point3D, מה שנמצא על המחסנית זה רק הכתובת שלו, וכיוון שכל הכתובות באותו גודל, אין בעיה שב p תהיה כתובת של Point3D. אבל מה יקרה בקטע הבא ( שאפשרי רק ב ++C): Point3D p3d(1,2,3); Point p = p3d;
Slicing Point3D p3d(1,2,3); Point p = p3d; _x _y _z p3d 2 1 _x _y p
Inheritance in C++ ב ++C לא קיים עץ ירושה יחיד ( אין אובייקט Object שכולם יורשים ממנו ), אלא מספר עצי ירושה. אפשר לרשת יותר ממחלקה אחת ב ++C אין ממשקים (interfaces) יש מחלקות אבסטרקטיות, אבל לא קיימת המילה השמורה abstract מחלקה נחשבת ' אבסטרקטית ' אם קיימת בה לפחות פונקציה אבסטרקטית אחת כדי שיהיה ניתן לדרוס פונקציה יש לציין זאת במפורש ( על ידי המילה השמורה virtual) כדי לציין שפונקציה היא אבסטרקטית יש לציין תחילה שהיא וירטואלית, ובנוסף להוסיף את האופרטור 0= בסוף החתימה שלה
Virtual method class Point{ protected: int _x; int _y; public: Point( int x, int y): _x(x), _y(Y){ } virtual void print(); void moveToOrigin(); }; #include “Point.h” #include Using namespace std; void Point:: print(){ cout _x _y<<endl; } void Point::moveToOrigin(){ this->_x = 0 ; this->_y = 0 ; } /* */ /* Point.h */ /* */ /* Point.cpp*/
class Point3D: public Point{ private: int _z; public: Point3D( int x, int y,int z); virtual void print(); void moveToOrigin(); }; … Point3D:: Point3d(int x, int y, int z): Point(x,y){ this->_z = z; } void Point3D:: print(){ cout _x _y; cout _z <<endl; } void Point3D::moveToOrigin(){ this->_x = 0 ; this->_y = 0 ; this->_z = 0; } /* */ /* Point3D.h */ /* */ /* Point3D.cpp*/ Virtual method
Late/Early Binding void main(){ Point *p2 = new Point3D(1,2,3); Point3D *p3 = new Point3D(4,5,6); p2->moveToOrigin(); P3->moveToOrigin(); p2->print(); p3->print(); delete p2; delete p3; } // call to Point:: moveToOrigin() // call to Point3D :: moveToOrigin() // call to Point3D :: print() Output: 0, 0, 3 0, 0, 0
Abstract class class Animal { protected: std::string name; int age; public: void printName(); virtual std::string say() =0; };
Multiple inheritance #include “animal.h” #include “drawing.h” class Snake : public Animal, public Drawing{ … }; Since there are no interfaces in C++, multiple inheritance is important and useful for big systems, especially when using design patterns
Ambiguities: class A { virtual void f(); } ; class B { virtual void f(); } ; class C : public A, public B { void f(); }; Solution : Each base class can be uniquely identified by using the scope resolution operator ::. C *c = new C(); c->A::f(); c->B::f(); Multiple inheritance- Pitfalls
Ninja Turtle NinjaTurtle Object The Diamond Problem: Solution : virtual inheritance class Turtle : virtual public Object class Ninja: virtual public Object
Summary Where do the objects live Java – primitive on the stack, objects on the heap C++ - Wherever you decide Slicing When polymorphism meets the stack Inheritance C++ has no interfaces No one single inheritance tree Can override only virtual function (or you will get shadowing) Multiple inheritance Doesn’t exist in Java, very useful in C++ Programmer should avoid ambiguities and diamond inheritance