תרגול מס' 11
Post on 02-Jan-2016
31 Views
Preview:
DESCRIPTION
TRANSCRIPT
11תרגול מס' הורשהפולימורפיזם
2מבוא לתכנות מערכות - 234122
הורשה
דוגמהתחביר יחסis-aהחלפת פונקציות
3מבוא לתכנות מערכות - 234122
דוגמה מנשק גרפי מורכב מרכיבים שונים
Widgetsהקרויים
תכונות לכל הרכיבים ישמשותפות
למשל רוחב, גובה, מיקום–
תכונות לרכיבים מסוימים ישייחודיות
פעולה המתבצעת לאחר לחיצה –Buttonעל
Entryקריאת הערך השמור ב-–
עצם מסוג חלון יצטרך לשמור אתכל הרכיבים הנמצאים בו
Radio button
Button
Entry
Check button
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// ...
};
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();// ...
};
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
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;
}
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 {
// ...
};
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
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ל-
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;
}
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 {};
};
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;
}
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;}
15מבוא לתכנות מערכות - 234122
הורשה סיכום
מחלקהB יכולה לרשת את התכונות של מחלקה A על ידי שימוש בהורשה
אםB-יורשת מ Aהיא מקבלת את כל השדות והמתודות שלה -לא ניתן לגשת מB לחלקים פרטיים של A-ניתן להגדיר שדות ומתודות כprotected כדי לאפשר למחלקה יורשת
לגשת אליהם אםB-יורשת מ A אז B is an A-וניתן להשתמש ב B-כ &A-וב &B-כ *A*אתחול ושחרור מחלקת הבסיס מתבצע כמו אתחול ושחרור של שדהמומלץ להסתיר את מימוש המחלקה ככל הניתן, גם ממחלקות יורשותניתן להגדיר מחדש מתודות של מחלקת האב במחלקת הבן
16מבוא לתכנות מערכות - 234122
פולימורפיזם
פונקציות וירטואליות מחלקות מופשטות(Abstract classes)חריגות והורשה
17מבוא לתכנות מערכות - 234122
פולימורפיזם
המתאים קוד יחיד )רב-צורתיות( מאפשרת יצירת פולימורפיזםלטיפוסים שונים
אםB נראה כמו A ומתנהג כמו AAאז הוא יוכל להחליף את
ניתן לכתוב קוד פולימורפי אשר יעבודAעבור טיפוס A אשר יורש מ-Bלכל טיפוס
נשתמש בהורשה כדי לייצג חיות שונות בעזרת מחלקות כך שנוכל לכתובקוד פולימורפי עבור כל סוגי החיות
משקל, חיה:גובה, ת. לידה, הדפס סוגי מזון,
השמע קול
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
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;
}};
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() {}// ...
};
אם מתכננים להשתמש
חייביםבפולימורפיזם ליצור הורס וירטואלי
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
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;}
};
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ב-
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}
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ב-
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";
}}
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!
28מבוא לתכנות מערכות - 234122
פולימורפיזם - סיכום
כדי לאפשר התנהגות פולימורפית למספר מחלקות היורשות ממחלקתאב משותפת משתמשים בפונקציות וירטואליות
כאשר מריצים פונקציה וירטואלית נבחרת הפונקציה המתאימה בזמןריצה
ניתן להשתמש בפולימורפיזם כדי להעליםif-ו switch מהקוד ולהחליף בקוד פשוט יותר וקל יותר להרחבה
ניתן להגדיר מחלקות אבסטרקטיות שחלק מהפונקציות שלהן הןוירטואליות טהורות
פונקציות וירטואליות דורשות שימוש במצביעים או רפרנסים נהוג להגדיר את כל החריגות בהיררכיה של הורשות כדי לאפשר תפיסה
של קבוצת חריגות כללית בקלות
29מבוא לתכנות מערכות - 234122
typeidהמרות ו-
-המרות בC++typeid
30מבוא לתכנות מערכות - 234122
Cהמרות ב-++-ניתן בC להמיר עם תחביר של ++C:או תחביר של בנאי
(Type)var או Type(var);שתי המרות אלו שקולות–
רבים: חסרונותלהמרות אלושל משמעויות:ערבוב המרות מסוג זה הן –
המרה בין מצביעים עם קשר הורשה ביניהם מוגדרת, אך המרה בין מצביעים שאין •קשר ביניהם אינה מוגדרת
( ללא קבוע - התוצאה יכולה להיות לא מוגדרתconstניתן בטעות להמיר עצם קבוע )•
התבצעה - המרות בין מצביעים מתקמפלות תמיד, אך משמעותן לדעת איזו המרה קשה –מהקומפיילרללא התראות עלולה להשתנות בגלל שינויים
אותןלחפשאת ההמרות בקוד או קשה לזהות –
-לשם כך מוגדרות בCהמרות מיוחדות הפותרות את בעיות אלו ++
31מבוא לתכנות מערכות - 234122
המרות
-בC:קיימים ארבעה סוגי המרות ++–static_cast ממירה בין שני עצמים בעזרת בנאי או בין מצביעים/רפרנסים שיש :
ביניהם קשר של הורשה
נכונות ההמרה נבדקת בזמן הקומפילציה•
בהמרה ממחלקת אב למחלקת בן המשתמש אחראי על הנכונות•
–dynamic_castממירה בין מצביעים/רפרנסים :
נכונות ההמרה נבדקת בזמן ריצה ומוחזרת שגיאה במקרה של כשלון•
–const_cast מסירה :constמהטיפוס
–reinterpret_castממירה בין של שני טיפוסים על ידי פירוש מחדש של הביטים :
משמשת למשחקי ביטים ודיבוג•
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);}
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);}
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;
}
35מבוא לתכנות מערכות - 234122
- סיכוםtypeidהמרות ו-
משימוש בהמרות ו- המנעוtypeid הם גורמים לקוד שקל להכניס בו - באגים וקשה לשנות אותו
כדי לבצע את מה שדרוש במקום השתמשו בפונקציות וירטואליותלבדוק איזה טיפוס יש לעצם
-אם חייבים לבצע המרה השתמשו בstatic_cast-אם חייבים להמיר ממחלקת אב לבן השתמשו בdynamic_castמשימוש ב-המנעו typeidאו מימוש מנגנון דומה בעצמכם מהמרות בסגנון של המנעו C
36מבוא לתכנות מערכות - 234122
העשרה - מימוש הורשה
מימוש יחס“is a”מימוש פונקציות וירטואליות
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העצם רואה בכל מקרה
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עצם מסוג
להמחשה בלבד
top related