תרגול מס' 11

38
ססססס סס'11 ההההה ההההההההההה

Upload: clementine-rush

Post on 03-Jan-2016

51 views

Category:

Documents


2 download

DESCRIPTION

תרגול מס' 11. הורשה פולימורפיזם. הורשה. דוגמה תחביר יחס is-a החלפת פונקציות. דוגמה. מנשק גרפי מורכב מרכיבים שונים הקרויים Widgets לכל הרכיבים יש תכונות משותפות למשל רוחב, גובה, מיקום לרכיבים מסוימים יש תכונות ייחודיות פעולה המתבצעת לאחר לחיצה על Button קריאת הערך השמור ב- Entry - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: תרגול מס' 11

11תרגול מס' הורשהפולימורפיזם

Page 2: תרגול מס' 11

2מבוא לתכנות מערכות - 234122

הורשה

דוגמהתחביר יחסis-aהחלפת פונקציות

Page 3: תרגול מס' 11

3מבוא לתכנות מערכות - 234122

דוגמה מנשק גרפי מורכב מרכיבים שונים

Widgetsהקרויים

תכונות לכל הרכיבים ישמשותפות

למשל רוחב, גובה, מיקום–

תכונות לרכיבים מסוימים ישייחודיות

פעולה המתבצעת לאחר לחיצה –Buttonעל

Entryקריאת הערך השמור ב-–

עצם מסוג חלון יצטרך לשמור אתכל הרכיבים הנמצאים בו

Radio button

Button

Entry

Check button

Page 4: תרגול מס' 11

4מבוא לתכנות מערכות - 234122

דוגמה

אם ננסה לממש מחלקות עבורWidgets:ניתקל במספר בעיות השוניםWidgetsבין ה-שכפול קוד –

Window בתוך Widgetsאין דרך נוחה להחזיק את כל ה-–

-איך היינו פותרים את הבעיה בC?מה החסרונות של פתרון זה ?

class Button {int x, y;int width,

height;string

text;//...

public:int

getWidth();int

getHeight();void

onClick();//...

};

class Entry {int x, y;int width,

height;string

text;//...

public:int

getWidth();int

getHeight();string

getValue();//...

};

class Window {Array<Button>

buttons;Array<Entry>

entries;// ... for

every type// ...

};

Page 5: תרגול מס' 11

5מבוא לתכנות מערכות - 234122

הורשה

כדי לציין בהורשהניתן להשתמש יורשת את התכונות שמחלקה מסוימת

של מחלקת בסיס )מחלקת אב( כלשהי

ניצור מחלקה עבורWidget אשר תייצג Widgetsאת התכונות המשותפות לכל ה-

כלWidget יירש את התכונות המשותפות ויוסיף את התכונות הייחודיות לו

class Entry : public Widget {

// ...public:

string getValue();// ...

};

class Button : public Widget {

// ...public:

void onClick();// ...

};

class Widget {int x, y, width,

height;string text;// ...

public:int getWidth();int getHeight();// ...

};

Page 6: תרגול מס' 11

6מבוא לתכנות מערכות - 234122

שמחלקה כדי לצייןB יורשת את מחלקה A נוסיף אחרי שם המחלקהB “: public A”

לעצם מטיפוסB יהיו את כל השדות בנוסף לאלו Aוהמתודות המוגדרים ב-

המוגדרים בו

נהוג לסמן הורשה בתרשימים על ידי חץהיוצא מהמחלקה היורשת אל מחלקת

האב, למשל:

הורשה - תחבירclass A {public:

int n;int f() { return

n; }};

class B : public A {public:

int m;int g() { return

m + n; }};

int main() {B b;b.n = 5;b.m = 4;cout << b.f() <<

endl;cout << b.g() <<

endl;return 0;

A

B Button Entry

Widget

Page 7: תרגול מס' 11

7מבוא לתכנות מערכות - 234122

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

נגישים ממחלקות יורשות-ניתן להגדיר שדות ומתודות כprotected מתודות ,

ושדות אלו יהיו:נגישים ממחלקות יורשות–

אינם נגישים משאר הקוד–

בעזרתprotected ניתן להגדיר מנשק נפרד לקוד המשתמש במחלקה על ידי הורשה מהמנשק

לקוד חיצוני

להסתיר את מימוש המחלקהכמו תמיד, נעדיף:public על protectedנעדיף –protected על privateנעדיף –

class A {int n;

protected:int f() { return

n; }public:

void set(int i) { n = i; }};

class B : public A {public:

int g() { return f(); }};

int main() {B b;b.n = 5; // errorb.set(5); // o.k.b.f(); // errorcout << b.g() <<

endl; return 0;}

Page 8: תרגול מס' 11

8מבוא לתכנות מערכות - 234122

בקרת גישה בהורשהA

private membersprotected members

public members

Bprivate members

protected members

public members

Cprivate members

protected members

public members

User code

class A {

// ...

};

 

class B : public A {

// ...

};

 

class C : public B {

// ...

};

Page 9: תרגול מס' 11

9מבוא לתכנות מערכות - 234122

is aהורשה - הורשה מממשת יחס“is a ” בין המחלקה

היורשת למחלקת האב–“a button is a widgetכפתור הוא רכיב גרפי - ”

אםB-יורש מ A נוכל להשתמש בעצם מטיפוס B בכל מקום בו ניתן להשתמש בעצם מטיפוס A–B& יוכל לשמש כ-A&

יכולה לשמש Bכתובת של עצם מסוג –Aככתובת של עצם מסוג

בעזרת הורשה נוכל להתייחס לעצמיםמטיפוסים שונים דרך החלק המשותף להם

–Array<Widget-יחיד יאחסן את כל ה >*widgets

class A {int n;

public:int f() { return

n; }void set(int i)

{ n = i; }};

class B : public A {public:

int g() { return f() + 1; }};

void h(A& a) {a.f();

}

int main() {B b;A& a = b; // o.k.A* ptra = new

B; // o.k. h(b);return 0;

}

B הוא סוג של A

Page 10: תרגול מס' 11

10מבוא לתכנות מערכות - 234122

הורשה - בנאים והורסיםבנאים והורסים אינם עוברים בירושה

מתבצע בדומה אתחול ושחרור מחלקת הבסיסלאתחול ושחרור שדות

של ברשימת האתחולB נקרא הבנאי של Aהשדותלפני מחלקת הבסיס מאותחלת –

)( A ייקרא Aאם לא מופיעה קריאה מפורשת לבנאי של –

)( תתקבל שגיאת קומפילציהAאם לא מוגדר בנאי •

–B אינו מאתחל שדות של A אתחולם מתבצע על ידי ,A

הריסת כל השדות של של לאחר B~ ייקרא A)(

=הנוצרים על ידי בנאי ההעתקה ואופרטור קוראים להעתקה/השמה של מחלקת הקומפיילר

האב

class A {

int n;

public:

A(int n) : n(n) {}

A() : n(0) {}

};

 

class B : public A {

int m;

public:

B(int n) : A(n), m(n) {}

B() : m(1) {}

};הקומפיילר יוסיף קריאה

)(Aל-

Page 11: תרגול מס' 11

11מבוא לתכנות מערכות - 234122

הורשה - זמני קריאה

?מה מדפיס הקוד הבאclass X {

int n;public:

X(int n) : n(n) {cout << "X::X():"

<< n << endl;}~X() {

cout << "X::~X():" << n << endl;

}};

class A {X x1;

public:A(int n) : x1(n) {

cout << "A::A()" << endl;

}~A() {

cout << "A::~A()" << endl;

}};

class B : public A {X x2;

public:B(int m, int n) :

A(m), x2(n) {cout <<

"B::B()" << endl; }~B() {

cout << "B::~B()" << endl;

}}; int main() {

B b(1, 2);cout << "=========" <<

endl;return 0;

}

Page 12: תרגול מס' 11

12מבוא לתכנות מערכות - 234122

MultiStackדוגמה - ברצוננו ליצור את המחלקהMultiStack אשר תומכת בכל הפעולות של

אשר מוציאה את popk( ובפעולה נוספת push, pop, topמחסנית רגילה )kהאיברים האחרונים שהוכנסו למחסנית

:קיימות שלוש דרכים לפתרון הבעיה מחדשMultiStackכתיבת –

ועבודה מיותרתשכפול קוד •

MultiStack כשדה של Stackשימוש ב-–

את המתודותלתפור" ידנית נצטרך "•

–MultiStack תירש את Stackלעבוד יוכל Stackקוד אשר עובד עם •

MultiStack גם עם

class Stack {int* data;int size;int nextIndex;

public:Stack(int size = 100);Stack(const Stack&

stack);~Stack();Stack& operator=(const

Stack& s);void push(const int& n);void pop();int& top();const int& top() const;int getSize() const;

class Full {};class Empty {};

};

Page 13: תרגול מס' 11

13מבוא לתכנות מערכות - 234122

MultiStack-עלינו לממש בMultiStack ,רק את הבנאי

popkוחריגה חדשה

האם התשובה משתנה אם השדהnextIndex -היה מוגדר כprotected?

-השימוש בprotected יכול לעזור ליורשים תלויות במימושאך מוסיף

מחשיפת בדרך כלל נעדיף להימנע –למחלקות יורשותהמימוש

class MultiStack : public Stack {public:

MultiStack(int size);void popk(int k);

class NotEnoughNumbers {};};

MultiStack::MultiStack(int size)

: Stack(size) {} void MultiStack::popk(int k) {

if (getSize() < k) {throw

NotEnoughNumbers();}for(int i = 0; i < k; +

+i ) {pop();

}}

void MultiStack::popk(int k) {if (nextIndex < k) {

throw NotEnoughNumbers();

}nextIndex -= k;

}

Page 14: תרגול מס' 11

14מבוא לתכנות מערכות - 234122

הגדרה מחדש של מתודות

להגדיר מחלקות יורשות יכולות גם פונקציות קיימותמחדש

overridingפעולה זו קרויה –

במקרה זה הפונקציה של מחלקתהאב מוסתרת על ידי הפונקציה

החדשה

כדי לקרוא לפונקציה הישנה מציינים של מחלקת האב namespaceאת ה-

לפני הקריאה לפונקציה

class LabeledPoint : public Point {string label;

public:LabeledPoint(string s, int

x, int y);void print() const;

};

void LabeledPoint::print() const {cout << label << ":

";Point::print();

}

void f() {LabeledPoint

p("origin", 0, 0);p.print(); // origin:

0,0}

class Point {int x, y;

public:Point(int x, int y);void print() const;

};

void Point::print() const {cout << x << "," << y <<

endl;}

Page 15: תרגול מס' 11

15מבוא לתכנות מערכות - 234122

הורשה סיכום

מחלקהB יכולה לרשת את התכונות של מחלקה A על ידי שימוש בהורשה

אםB-יורשת מ Aהיא מקבלת את כל השדות והמתודות שלה -לא ניתן לגשת מB לחלקים פרטיים של A-ניתן להגדיר שדות ומתודות כprotected כדי לאפשר למחלקה יורשת

לגשת אליהם אםB-יורשת מ A אז B is an A-וניתן להשתמש ב B-כ &A-וב &B-כ *A*אתחול ושחרור מחלקת הבסיס מתבצע כמו אתחול ושחרור של שדהמומלץ להסתיר את מימוש המחלקה ככל הניתן, גם ממחלקות יורשותניתן להגדיר מחדש מתודות של מחלקת האב במחלקת הבן

Page 16: תרגול מס' 11

16מבוא לתכנות מערכות - 234122

פולימורפיזם

פונקציות וירטואליות מחלקות מופשטות(Abstract classes)חריגות והורשה

Page 17: תרגול מס' 11

17מבוא לתכנות מערכות - 234122

פולימורפיזם

המתאים קוד יחיד )רב-צורתיות( מאפשרת יצירת פולימורפיזםלטיפוסים שונים

אםB נראה כמו A ומתנהג כמו AAאז הוא יוכל להחליף את

ניתן לכתוב קוד פולימורפי אשר יעבודAעבור טיפוס A אשר יורש מ-Bלכל טיפוס

נשתמש בהורשה כדי לייצג חיות שונות בעזרת מחלקות כך שנוכל לכתובקוד פולימורפי עבור כל סוגי החיות

משקל, חיה:גובה, ת. לידה, הדפס סוגי מזון,

השמע קול

Page 18: תרגול מס' 11

18מבוא לתכנות מערכות - 234122

פולימורפיזם ניצור את המחלקהAnimal ואת

אשר תירש ממנהDogהמחלקה

?מה ידפיס הקוד הבא

:בעיההקומפיילר בוחר איזו פונקציה תיקרא –

בזמן הקומפילציה )קישור סטטי(

הפונקציה שאנו רוצים שתרוץ תלויה –בטיפוס בזמן הריצה

class Animal {int age, weight;

public:Animal(int age, int

weight);int getAge() const;void makeSound() const

{cout << endl;

} // no sound by default;};

class Dog: public Animal {public:

Dog(int age, int weight);

void makeSound() const {

cout << "vuf vuf" << endl;

}};

Dog* d = new Dog(3,4);

Animal* a = d;

a->makeSound();

d->makeSound();

Animal::makeSound

Dog::makeSound

Page 19: תרגול מס' 11

19מבוא לתכנות מערכות - 234122

פונקציות וירטואליות

כוירטואליתניתן להכריז על פונקציה במחלקת האב:

ייצור קוד במקרה זה הקומפיילר –הפונקציה אשר יבחר את

המתאימה לטיפוס בזמן הריצה)קישור דינאמי(

כעת בקריאהa->makeSound)( תתבצע הפונקציה המתאימה

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

היורשות

class Animal {int age, weight;

public:Animal(int age, int

weight);int getAge() const;virtual void

makeSound() const {cout << endl;

} // no sound by default;};

class Dog: public Animal {public:

Dog(int age, int weight);

void makeSound() const {

cout << "vuf vuf" << endl;

}};

Page 20: תרגול מס' 11

20מבוא לתכנות מערכות - 234122

פונקציות וירטואליותclass Dog: public Animal {public:

Dog(int age, int weight);

void makeSound() const {

cout << "vuf vuf" << endl;

}};

class Cat: public Animal {public:

Cat(int age, int weight);

void makeSound() const {

cout << "miao" << endl;

}};

class Fish: public Animal {public:

Fish(int age, int weight);}; // the default makeSound is OK

void foo() {Animal* animals[3];animals[0] = new

Dog(3,4);animals[1] = new

Fish(1,1);animals[2] = new

Cat(2,2);for (int i = 0; i < 3;

++i) {animals[i]-

>makeSound();delete

animals[i];}

}

vuf vuf miao

מה ייקרא כאן?

class Animal {int age, weight;

public:Animal(int age, int

weight);virtual ~Animal() {}// ...

};

אם מתכננים להשתמש

חייביםבפולימורפיזם ליצור הורס וירטואלי

Page 21: תרגול מס' 11

21מבוא לתכנות מערכות - 234122

מחלקות אבסטרקטיות

במקרים רבים מחלקת האב אינה טיפוס שלם בפני עצמהבסוף הכרזתה” 0“= על ידי הוספת pure virtualניתן להגדיר פונקציה כ-–

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

מחלקה המכילה פונקציה וירטואליתמחלקה אבסטרקטיתטהורה נקראת

לא ניתן ליצור עצם מטיפוס המחלקה–

חייבים ליצור עצם מטיפוס היורש ממנה–

מצביעים ורפרנסיםניתן ליצורלמחלקות אבסטקרטיות

למעשה שימוש בפונקציה וירטואלית משמעותי רק עבור–מצביעים או רפרנסים

class Shape {int center_x, center_y;

public:Shape(int x, int y) :

center_x(x), center_y(y) {}

virtual ~Shape() {}virtual double area()

const = 0;};

area היא פונקציה היא מחלקה Shapeוירטואלית טהורה

אבסטרקטית, לא ניתן ליצור עצם מטיפוס

Shape

Page 22: תרגול מס' 11

22מבוא לתכנות מערכות - 234122

מחלקות אבסטרקטיות

class Square : public Shape {int edge;

public:Square(int x, int y, int

edge) :Shape(x,y),

edge(edge) {}virtual double area()

const {return edge*edge;

}};

void foo() {Shape* shapes[N]; // an array of squares &

circles// initialization ...double totalArea=0; for (int i = 0; i < N; +

+i) {totalArea +=

shapes[i]->area(); }cout << totalArea <<

endl;} 

class Shape {int center_x, center_y;

public:Shape(int x, int y) :

center_x(x), center_y(y) {}

virtual ~Shape() {}virtual double area()

const = 0;};

class Circle : public Shape {int radius;

public:Circle(int x, int y, int

radius) :Shape(x,y),

radius(radius) {}virtual double area()

const {return

radius*radius*PI;}

}; 

Page 23: תרגול מס' 11

23מבוא לתכנות מערכות - 234122

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

מהפעולה הנשלחת לעצם ההודעהשמתבצעת בפועל

גורמת לחישוב שונה לכל צורהareaההודעה –

במקום לשאול עצם מיהו ולהגיד לו מהלעשות בהתאם - פשוט שולחים לו את

ההודעה והיא תפורש בהתאם לזהותו

קוד אשר משתמש בפולימורפיזם מחליף בקריאות לפונקציות switch ו-ifשימוש ב-

וירטואליותקצר יותר ונקי יותרקור –

מקרים חדשיםקל להוסיף –

class Widget {//...virtual void redraw();

}; class Window {

Array<Widget*> children;//...

public:void redraw();

}; void Window::redraw() {

for(int i=0;i<children.size();++i) {

children[i]->redraw();

}}

חדש Widgetהוספת אינה דורשת שינויים

Windowב-

Page 24: תרגול מס' 11

24מבוא לתכנות מערכות - 234122

פולימורפיזם והעתקת עצמים

פולימורפיזם והעברהby-value )אינם משתלבים )או העתקת עצמים בכלל:

“ עצם המוחזקby valueכלומר לא( ”בעלכרפרנס או מצביע( לא יכול להיות

טיפוס שונה בזמן הריצה

-שימוש בcopy c’tor לפי שמו יוצר עצם מהטיפוס המוגדרa של Animalבהתבסס על חלק ה- חדש Animal יוצרת Animalלכן העתקת –אבסטרקטית Shape כי s של Shapeלא ניתן ליצור העתק –

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

void foo(Animal& a, Shape& s) {Animal copy =

Animal(a);copy.makeSound();a.makeSound();

Shape copy2 = Shape(s); // error}

Page 25: תרגול מס' 11

25מבוא לתכנות מערכות - 234122

חריגות ופולימורפיזםהשימוש בפולימורפיזם

מאפשר הגדרת היררכיהשל חריגות

ניתן לתפוס חריגות לפי מחלקתהאב שלהן וכך לאפשר תפיסה

של חריגות ספציפיות או חריגות מקבוצהכללית יותר

?חשוב לתפוס חריגות עם &, למה

הספריה הסטנדרטית מגדירה מספר חריגותstd::exceptionשלכולן אב משותף -

exceptionמומלץ שחריגות יירשו את –)בעקיפין או ישירות(

class Stack {// ...class Exception : public

std::exception {};class Full : public Exception {};class Empty : public Exception

{};};

void f() {try {

Stack s(100);do_stuff(s);

} catch (Stack::Full& e) {

cerr << "Not enough room";

} catch (Stack::Exception& e) {

cerr << "Error with stack";

} catch (std::exception& e) {

cerr << e.what() << endl;

}}

what היא פונקציה וירטואלית המוגדרת

std::exceptionב-

Page 26: תרגול מס' 11

26מבוא לתכנות מערכות - 234122

חריגות ופולימורפיזם

אם מספר כלליcatch מתאימים לתפיסת חריגה ייבחר מופיע ראשוןהכלל אשר

לכן מתחילים מהמקרה הספציפי ביותר עד לכללי ביותר –)לכן שימוש ב-“...” תמיד יופיע אחרון(

?מה יודפס בכל אחת מהפונקציות

class AException {};class BException :

public AException {};class CException :

public AException {};

void f() {try {

throw CException();

} catch (BException& b) {

cout << "B caught";

} catch (CException& c) {

cout << "C caught";

} catch (AException& a) {

cout << "A caught";

}}

void g() {try {

throw CException();

} catch (BException& b) {

cout << "B caught";

} catch (AException& a) {

cout << "A caught";

} catch (CException& c) {

cout << "C caught";

}}

void h() {try {

throw AException();

} catch (BException& b) {

cout << "B caught";

} catch (CException& c) {

cout << "C caught";

} catch (AException& a) {

cout << "A caught";

}}

Page 27: תרגול מס' 11

27מבוא לתכנות מערכות - 234122

שימוש נכון בהורשה כאשר יוצרים מחלקהB היורשת את A:חשוב להקפיד

B יהיה ניתן להשתמש ב-Aבכל מקום שבו ניתן להשתמש ב-

נניח שנרצה להוסיף מחלקה עבור אליפסההאם עיגול הוא סוג של אליפסה?–

עיגול לא יכולsetAB, בגלל המתודה לא–לעשות את כל מה שאליפסה עושה

עללהוסיףהמחלקה היורשת צריכה התנהגות מחלקת האב

לא להסתיר–

לא לשנות–

במקרה שלנוEllipse-ו Circle לרשת צריכותShapeישירות את

class Ellipse : public Shape {int a,b;

public:Ellipse(int x, int y, int

a, int b);virtual double area()

const;void setAB(int a, int b);

}; class Circle : public Ellipse {public:

Circle(int x, int y, int radius) :

Ellipse(x,y,r,r) {}};void break_stuff(Ellipse& e) {

e.setAB(1,2);}

e יכול להיות Circle!

Page 28: תרגול מס' 11

28מבוא לתכנות מערכות - 234122

פולימורפיזם - סיכום

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

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

ניתן להשתמש בפולימורפיזם כדי להעליםif-ו switch מהקוד ולהחליף בקוד פשוט יותר וקל יותר להרחבה

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

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

של קבוצת חריגות כללית בקלות

Page 29: תרגול מס' 11

29מבוא לתכנות מערכות - 234122

typeidהמרות ו-

-המרות בC++typeid

Page 30: תרגול מס' 11

30מבוא לתכנות מערכות - 234122

Cהמרות ב-++-ניתן בC להמיר עם תחביר של ++C:או תחביר של בנאי

(Type)var או Type(var);שתי המרות אלו שקולות–

רבים: חסרונותלהמרות אלושל משמעויות:ערבוב המרות מסוג זה הן –

המרה בין מצביעים עם קשר הורשה ביניהם מוגדרת, אך המרה בין מצביעים שאין •קשר ביניהם אינה מוגדרת

( ללא קבוע - התוצאה יכולה להיות לא מוגדרתconstניתן בטעות להמיר עצם קבוע )•

התבצעה - המרות בין מצביעים מתקמפלות תמיד, אך משמעותן לדעת איזו המרה קשה –מהקומפיילרללא התראות עלולה להשתנות בגלל שינויים

אותןלחפשאת ההמרות בקוד או קשה לזהות –

-לשם כך מוגדרות בCהמרות מיוחדות הפותרות את בעיות אלו ++

Page 31: תרגול מס' 11

31מבוא לתכנות מערכות - 234122

המרות

-בC:קיימים ארבעה סוגי המרות ++–static_cast ממירה בין שני עצמים בעזרת בנאי או בין מצביעים/רפרנסים שיש :

ביניהם קשר של הורשה

נכונות ההמרה נבדקת בזמן הקומפילציה•

בהמרה ממחלקת אב למחלקת בן המשתמש אחראי על הנכונות•

–dynamic_castממירה בין מצביעים/רפרנסים :

נכונות ההמרה נבדקת בזמן ריצה ומוחזרת שגיאה במקרה של כשלון•

–const_cast מסירה :constמהטיפוס

–reinterpret_castממירה בין של שני טיפוסים על ידי פירוש מחדש של הביטים :

משמשת למשחקי ביטים ודיבוג•

Page 32: תרגול מס' 11

32מבוא לתכנות מערכות - 234122

static_castהמרות - המרה סטטית מאפשרת המרה בין

עצמים בעזרת המרה מוגדרת)ע"י בנאי או אופרטור המרה(

במקרה שאין המרה מוגדרת תתקבל–שגיאת קומפילציה )בדומה להמרה

רגילה(

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

תתקבל שגיאת קומפילציהבהמרה סטטית אם אין קשר בין הטיפוסים –

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

void f(int& n, A& base) {

double d = static_cast<double>(n);

B& derived = static_cast<B&>(base);

B* ptr = static_cast<B*>(&base);

double* ptr2 = static_cast<double*>(&n);

C& unrelated = static_cast<C&>(base);}

Page 33: תרגול מס' 11

33מבוא לתכנות מערכות - 234122

dynamic_castהמרות - המרה דינאמית משמשת להמרה בין מצביעים

של תוך כדי בדיקה בזמן ריצה ורפרנסים נכונות ההמרה

יוחזר מצביעיםשל בהמרה דינאמית NULL אם ההמרה אינה אפשרית

תיזרק רפרנסיםשל בהמרה דינאמית std::bad_castאם ההמרה אינה אפשרית

טיפוסים המרה דינאמית עובדת רק על)בעלי פונקציה וירטואלית אחת פולימורפיים

לפחות(

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

void g(int n, A& base, C& c) {

B* ptr = dynamic_cast<B*>(&base);

if (ptr == NULL) {

cerr << "base is not a B";

}

try {

B& derived = dynamic_cast<B&>(base);

} catch (std::bad_cast& e) {

cerr << "base is not a B";

}

double* ptr2 = dynamic_cast<double*>(&n);

A& unrelated = dynamic_cast<A&>(c);}

Page 34: תרגול מס' 11

34מבוא לתכנות מערכות - 234122

typeid האופרטורtypeid מקבל שם טיפוס

או ערך ומחזיר עצם מטיפוס type_infoהמתאר את שם הטיפוס

type_infoניתן להשוות –ניתן לקבל מחרוזת המתארת את –

שם טיפוס )טוב למטרות דיבוג(

להימנע משימוש ב-מומלץtypeidשימוש בשאלה "איזה טיפוס –

אתה?" ובחירה בקוד לפיו מחזירה את החסרונות של קוד ללא

פולימורפיזם

#include <typeinfo>//...class Base {

virtual void v() {}};class Derived : public Base {};

void f() {Base* ptrb = new Base;Base* ptrd = new Derived;cout << typeid(ptrb).name()

<< endl;cout << typeid(ptrd).name()

<< endl;cout <<

typeid(*ptrb).name() << endl;cout <<

typeid(*ptrd).name() << endl;if (typeid(*ptrd) !=

typeid(Base)) {cout << "diff";

}return 0;

Page 35: תרגול מס' 11

35מבוא לתכנות מערכות - 234122

- סיכוםtypeidהמרות ו-

משימוש בהמרות ו- המנעוtypeid הם גורמים לקוד שקל להכניס בו - באגים וקשה לשנות אותו

כדי לבצע את מה שדרוש במקום השתמשו בפונקציות וירטואליותלבדוק איזה טיפוס יש לעצם

-אם חייבים לבצע המרה השתמשו בstatic_cast-אם חייבים להמיר ממחלקת אב לבן השתמשו בdynamic_castמשימוש ב-המנעו typeidאו מימוש מנגנון דומה בעצמכם מהמרות בסגנון של המנעו C

Page 36: תרגול מס' 11

36מבוא לתכנות מערכות - 234122

העשרה - מימוש הורשה

מימוש יחס“is a”מימוש פונקציות וירטואליות

Page 37: תרגול מס' 11

37מבוא לתכנות מערכות - 234122

העשרה - מימוש הורשה כיצד יכול הקומפיילר לממש את

” ?B is an A“ההתנהגות של

:מבנה העצם בזיכרון ייראה כךתחילה השדות של מחלקת האב–

אחריהם השדות הנוספים של מחלקת –הבן

מבנה זה מאפשר לקוד המקבל אתלהתייחס אליו כתובת תחילת העצם

כאל עצם ממחלקת האב

כל השדות עבור עצם של מחלקת האבנמצאים במקומם

class A {

int a;

int b;public:

int f();

int g();};

A Bb c da

class B : public A {

int c;int d;

public:int h();

};

Bעצם מסוג

Aba

Aעצם מסוג

מי שמסתכל על תחילת Aהעצם רואה בכל מקרה

מי שמסתכל על תחילת Aהעצם רואה בכל מקרה

Page 38: תרגול מס' 11

38מבוא לתכנות מערכות - 234122

Avptr

העשרה - פונקציות וירטואליות כדי לאפשר קריאה לפונקציה הנכונה

בזמן ריצה הקומפיילר משתמש במצביעים לפונקציות

לכל מחלקה נשמרת טבלה )הקרויהvtbl עם מצביעים עבור הפונקציות )

הוירטואליות המתאימות ביותר לטיפוס

התא הראשון של כל עצם יכיל מצביע( אשר יצביע לטבלה vptr)הקרוי

המתאימה ביותר לעצם

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

class A {int a;int b;

public:virtual

int f();virtual

int g();};

Bb c da

Bעצם מסוג

class B : public A {

int c;int d;

public:virtual

int f();};

A::f

A::g

vtbl for A

B::f

A::g

vtbl for B

void call_f(A* const this) {

this->vptr[0](this);}

Avptr ba

Aעצם מסוג

להמחשה בלבד