5.1.1. דוגמא – הורשה ו-Overriding

נציג כעת סוג מורחב יותר של הורשה – Non strict inheritance.

Strict inheritance איפשר להרחיב את המחלקה המקורית על ידי הוספת שדות ופונקציות בלבד. Non strict inheritance ייתן לנו כוח נוסף.

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

class Array

{

public:

      //

      // Ctor, Dtor

      //

      explicit Array(int len_) :

            len(len_), buff(new int[len]) {}

      ~Array(void) { delete[] buff; }

      //

      // Public Methods and operators

      //

      int size(void) const { return len; }

      int& operator[](int i)

      {

            return buff[i];

      }

      int operator[](int i) const

      {

             return buff[i];

      }

private:

      const int len;

      int* const buff;

      Array(const Array&);

      Array& operator=(const Array&);

};



מעט הסברים על הקוד

השימוש במילה explicit:  בנאי (constructor) המקבל פרמטר אחד משמש בשפת C++ כאופרטור המרה. אם הבנאי מוגדר כ-explicit אז הוא אינו משמש כאופרטור המרה להמרה סמויה.

נניח שקיימת פונקציה עם החתימה:

void f(Array arr);

המצב הבא הוא תקין. הפונקציה מקבלת ערך מסוג Array:

Array arr(10);

...

f(arr);

המצב אותו אנו רוצים למנוע הוא מקרה שמשתמש יקרא בטעות לפונקציה עם int, לדוגמא:

f(3);

אם המשתמש באמת רוצה לקרוא לפונקציה עם מערך חדש בגודל 3, אז במידה ואנחנו משתמשים במילה explicit הוא יצטרך לכתוב זאת כך:

f(Array(3));

Copy Ctor ואופרטור= הממומשים כ-private

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

נרצה כעת ליצור מחלקה חדשה, שתהיה מערך לו קיימת בדיקת גבולות.

על מנת לעשות שימוש חוזר בקוד, נירש מהמחלקה Array. בעמוד הבא נציג את קוד המחלקה החדשה.

class CheckedArray: public Array

{

      class RangeError {};

public:

      explicit CheckedArray(int len) : Array(len) {}

      int& operator[](int) throw (RangeError);

};

int& CheckedArray :: operator[](int i)

      throw (CheckedArray :: RangeError) 

{

      if (0 > i || i >= size()) throw RangeError();

      return Array :: operator[] (i);

}

ההורשה בדוגמא הינה הורשת public, כלומר – כל מה שהיה public במחלקה המקורית, הוא גם ה-public של המחלקה החדשה. כל מה שהיה protected במחלקה המקורית, הוא גם protected במחלקה החדשה.

נשים לב שבהורשה ה-constructor אינו נורש אל המחלקה החדשה. המחלקה החדשה חייבת להגדיר constructor משלה שיקרא ל-constructor המתאים של Array, וזאת מכיוון של-Array אין בנאי חסר פרמטרים.

גם בנאי המחלקה החדשה מוגדר כ-explicit, מאותם נימוקים שבכללם Array היה מוגדר כזה.

מאת: ניצן

Borland style vptr

לפי מה שאני מכיר:
"חסרון בגישה זו: גם כאשר איננו משתמשים ב-dynamic binding – אנחנו משלמים במקום"
לא נכון , עבור מחלקה A שאין לה מתודות דינמיות לא יווצר כלל המצביע, ולמשל עבור מחלקה B שיורשת מA פשוט נוסיף בהתחלה את המצביע, ואחרי הבלוק של A את שאר האינפורמציה של B . וככה לא משלמים על מה שלא משתמשים ועקרונות C++ נשמרים.
מה שכן באמת הcasting קצת יותר מסובך....
שיתוף:
| עוד