תכנות מונחה עצמים והנדסת תוכנה –  תשע"ד
This presentation is the property of its rightful owner.
Sponsored Links
1 / 31

תכנות מונחה עצמים והנדסת תוכנה – תשע"ד PowerPoint PPT Presentation


  • 131 Views
  • Uploaded on
  • Presentation posted in: General

תכנות מונחה עצמים והנדסת תוכנה – תשע"ד. העמסה של אופרטורים ( Operator overloading ). מבוא. מטרה: להכליל אופרטורים של טיפוסים בסיסיים לפעול על עצמים. למשל, הכללת האופרטור "+" עבור מספר מרוכב, וקטור ורשימה משורשרת. מתקבל קוד ברור. האופרטורים מתפקדים בקוד כרגישים להקשרם.

Download Presentation

תכנות מונחה עצמים והנדסת תוכנה – תשע"ד

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.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


3222618

תכנות מונחה עצמים והנדסת תוכנה – תשע"ד

העמסה של אופרטורים (Operator overloading)


3222618

מבוא

מטרה: להכליל אופרטורים של טיפוסים בסיסיים לפעול על עצמים.

למשל, הכללת האופרטור "+" עבור מספר מרוכב, וקטור ורשימה משורשרת.

מתקבל קוד ברור.

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

ניתן להכליל את מרבית האופרטורים הקיימים אך לא ניתן להוסיף אופרטורים חדשים (Deitel and Deitel chapter 8).

רשימה חלקית של אופרטורים הניתנים להכללה: +,-,/,%,^,&,|,~,=,<,>,=+,=-,>>,<<,==,new,delete,<-.

אופרטורים שאינם ניתנים להכללה: .,::,: ?.

2


3222618

מבוא (המשך)

קוד ללא שימוש באופרטורים:

Complex z1(1,2), z2(2,3), z3;

z3 = z1.add(z2);

z3.print();

z1.mul(z2).print();

Complex Complex::add(Const Complex& other) const {

return Complex(_real+other._real, _imag+other._imag);

}

3


3222618

מבוא (המשך)

שימוש באופרטורים:

Complex z1(1,2), z2(2,3), z3;

z3 = z1+z2;

cout << z3;

cout << z1*z2;

Complex Complex::operator+(Const Complex& other) const {

return Complex(_real+other._real, _imag+other._imag);

}

z3 = z1+z2  z3 = z1.operator+(z2)

cout << z3  operator<<( cout,z3 )

4


3222618

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

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

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

class Complex{

public:

Complex operator+(const Complex& other) const;

:

};

Complex Complex::operator+(Const Complex& other) const {

return Complex(_real+other._real, _imag+other._imag);

}

5


3222618

אופרטורים גלובליים

אופרטור כפונקציה גלובלית חברה:

class Complex {

friend Complex operator+(const Complex& z1,

const Complex& z2);

:

};

Complex operator+(const Complex& z1,const Complex& z2) ;

Complex operator+(const Complex& z1,const Complex& z2) {

return Complex(z1._real+z2._real,z1. _imag+z2._imag);

}

z3 = z1+z2  z3 = operator+(z1,z2)

z1+z2+z3  operator+( operator+(z1,z2) , z3 )

6


3222618

אופרטורים גלובליים (המשך)

אופרטור כפונקציה גלובלית לא חברה (תוך שימוש בפונקציות גישה):

Complex operator+(const Complex& z1,const Complex& z2) ;

Complex operator+(const Complex& z1,const Complex& z2) {

return Complex(z1.getReal()+z2.getReal(),

z1. getImag()+z2.getImag());

}

z3 = z1+z2  z3 = operator+(z1,z2)

z1+z2+z3  operator+( operator+(z1,z2) , z3 )

7


3222618

אופרטורים גלובליים (סיכום ביניים)

לפי עקרון האנקפסולציה נעדיף אופרטורים גלובליים שאינם חברים (ונעזרים בפונקציות גישה בסיסיות) על פני אופרטורים לוקליים. באופן זה, פחות פונקציות יקבלו גישה לשדות פרטיים.

זאת בתנאי שניתן לממש בעזרת פונקציות גישה בסיסיות (ללא פונקציות גישה מיותרות ו"חושפניות") ובתנאי שהיעילות לא נפגמת במידה רבה (ללא העתקות רבות).

בנוסף, נעדיף להגדיר אופרטורים גלובליים לטיפול באפשרויות רבות על פני הגדרות של אופרטורים מקומיים רבים.

8


3222618

אופרטורים גלובליים (סיכום ביניים)

קיימים מקרים בהם מוכרחים להגדיר אופרטורים גלובליים:

למשל: double + Complex ,operator<<

גם ההיפך נכון, קיימים אופרטורים שלא ניתן להגדיר אותם כגלובליים (המרה =, [], (), ->, ).

הגדרת שני אופרטורים לאותה הפעולה (אחד במחלקה ואחד גלובלי) תגרום לשגיאת קומפילציה (ambiguous error).

9


3222618

אופרטורים של קלט ופלט

std::ostream היא מחלקה המייצגת זרם פלט (output stream).

std הוא מרחב השם (name space) של הספרייה הסטנדרטית.

std::cout הוא עצם גלובלי מהמחלקה std::ostream.

std::cout מייצג זרם פלט לערוץ הפלט הסטנדרטי (מחרוזת שתופיע על המסך).

שימוש באופרטור >> הקיים בספריה הסטנדרטית:

std::cout << 5  std::cout.operator<<(5)

std::ostream& std::ostream::operator<<(int n)

10


3222618

אופרטורים של קלט ופלט (המשך)

העמסת האופרטור << עבור מחלקת Complex.

הפעלה ע"י std::cout<<z מחייבת הגדרה גלובלית של האופרטור.

הגדרת האופרטור:

std::ostream& operator<<( std::ostream& os,

const Complex& z) {

os << z.getReal() << “+” << z.getImag() << “*i”;

return os;

}

std::cout << z1 << z2 

operator<<( operator<<(std::cout,z1) , z2 )

לצורך שרשור

שימוש באופרטור << ביחס לטיפוסים: double, מחרוזות

11


3222618

אופרטורים של קלט ופלט (המשך)

אופרטור הקלט >> יוגדר באותו האופן.

דוגמא 8.3.

12


3222618

אופרטורים אונריים

אופרטור הפועל על עצם יחיד.

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

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

דוגמא:

Complex Complex::operator-() const {

return Complex(-getReal(),-getImag());

}

Complex operator-( const Complex& z ) {

return Complex(-z.getReal(),-z.getImag());

}

z1 = -z1  z1 = z1.operator-();

z1 = operator-(z1);

13


3222618

אופרטורים של השוואה

אינו מוגדר באופן ברירת מחדל.

דוגמא:

bool operator==( const Complex& l, const Complex& r ) const {

return (l.getReal()==r.getReal() && l.getImag()==r.getImag());

}

bool operator!=(const Complex& l, const Complex& r ) const {

return !(l==r);

}

14


3222618

דוגמא: מחלקת מערך

  • מערכים ב-C++ ממומשים כמצביעים.

  • חסרונות:

    • חסרה בדיקת תחום.

    • אין אפשרות להשוות בין שני מערכים בעזרת אופרטור ==.

    • אין אפשרות להציב מערך בתוך מערך (מצביע קבוע).

    • אין אפשרות לכתוב או לקרוא מערך שלם בעזרת האופרטורים >>,<<.

15


3222618

דוגמא: מחלקת מערך (המשך)

  • מימוש מחלקת מערך כולל:

    • בדיקת תחום.

    • אופרטורים של השוואה.

    • אופרטור השמה ובנאי העתקה.

    • אופרטור כתיבה וקריאה.

    • עצם ממחלקת מערך יכיר את גודלו.

    • אופרטור המתייחס לתא במערך (אופרטור [] כפי שקיים עבור מערכים).

  • דוגמא 8.4_6 (חלוקת האופרטורים בדוגמא לגלובליים/פונקציות מחלקה/חברות אינה תמיד תואמת את הכללים שלנו).

16


3222618

דוגמא: מחלקת מערך (המשך)

הערות:

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

  • הגדרת אופרטור השמה:

    Array& Array::operator=(const Array& right)

    • החזרת הפניה היא יעילה יותר (ביחס להחזרת ערך).

    • בדוגמא של Deitel אופרטור ההשמה החזיר const& זאת בכדי למנוע את הפעולה המלאכותית הבאה: (x=y)=z. בספרייה הסטנדרטית זה לא נהוג.

    • החזרת הפניה מאפשרת שרשור עבור אופרטורים הפועלים משמאל לימין (למשל האופרטור >> אבל לא אופרטור =).


3222618

דוגמא: מחלקת מערך (המשך)

הגדרת אופרטור []:

int& Array::operator[](int subscript)

const int& Array::operator[](int subscript) const

מאפשר שימוש דומה למערך.

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

18


3222618

דוגמא: מחלקת מחרוזת

  • ניתן לייצג מחרוזת ע"י:

    • char* (שפת C).

    • הגדרת מחלקה String (שפת C++).

    • שימוש במחלקה קיימת std::string (הספרייה הסטנדרטית של C++).

  • הגדרת אופרטור ():

    String String::operator() (int,int) const

    • התייחסות לעצם כאל פונקציה (function object).

  • דוגמא 8.7_9(חלוקת האופרטורים בדוגמא לגלובליים/פונקציות מחלקה/חברות אינה תמיד תואמת את הכללים שלנו).


3222618

אופרטורים של המרות

  • המרות בין מחלקה A לטיפוס בסיסי.

  • המרות בין מחלקה A למחלקה B.

  • המרות בין טיפוס בסיסי אחד לטיפוס בסיסי שני – לא ניתן.

  • מימוש בעזרת בנאי ואופרטור המרה.

  • דוגמא:

    A::A(int n) // cast from int to A

    A::operator int() const // cast from A to int

    • אין צורך בהגדרת ערך החזרה.

    • ניתן להגדיר אך ורק במחלקה (ולא באופן גלובלי).


3222618

אופרטורים של המרות (המשך)

  • שימוש באופרטור המרה:

    A a;

    cout << (int)a; // C syntax

    cout << int(a); // C++ syntax

    cout << a.operator int(); // direct syntax

  • שימוש עקיף באופרטור המרה:

    cout << a;

    • הקומפיילר ידאג לקרוא לאופרטור המרה.

    • אילו האופרטור << היה ממומש לעצם ממחלקה A אז הייתה קריאה אליו.

    • אילו היו ממומשים כמה אופרטורי המרה (ל-int וגם ל-double) אז הייתה מתקבלת הודעת שגיאה עבור: cout << a;

    • הקומפיילר לא יבצע הרכבה של שתי המרות עקיפות (למשל מ-B ל-A ואחר כך מ-A ל-int).


3222618

אופרטורים של המרות (המשך)

  • המרות בין שתי מחלקות שאינן בסיסיות:

    A::A(const B& b)// cast from B to A

    A::operator B() const // cast from A to B


3222618

אופרטור ++,--

  • אופרטור prefix (++x) – מחזיר את הערך שהתקבל אחרי ההוספה.

  • אופרטור postfix (x++) – מחזיר את הערך שהיה לפני ההוספה.

  • מוגדרים באופן שונה כדי שהקומפיילר יבדיל ביניהם.

  • הגדרת אופרטור prefix:

    Date& Date::operator++() // local

    Date& operator++(Date&) // global

    • מחזיר הפניה (לעצם המעודכן).

  • שימוש באופרטור prefix:

    Date d;

    ++d;

    d.operator++(); // using local operator

    operator++(d); // using global operator


3222618

אופרטור ++,-- (המשך)

  • הגדרת אופרטור postfix:

    Date Date::operator++(int) // local

    Date operator++(Date&,int) // global

    • הגדרה בעזרת פרמטר “dummy” כדי להבדיל.

    • מחזיר ערך ולא הפניה (ערך ההחזרה הוא עצם זמני).

  • שימוש באופרטור postfix:

    Date d;

    d++;

    d.operator++(0); // using local operator

    operator++(d,0); // using global operator

  • דוגמא 8.10_12


3222618

מחרוזת ומערך של הספרייה הסטנדרטית

  • מחלקת מחרוזת (string) של הספרייה הסטנדרטית:

    דוגמא 8.13

    אופרטורים ופונקציות של string

  • מחלקת מערך (vector) של הספרייה הסטנדרטית:

    דוגמא 8.14

    אופרטורים ופונקציות של vector


3222618

העמסה של אופרטורים - סיכום

  • הדגמנו את האופרטורים הבאים:

    • בינאריים (+,-,*,/,=+,=-,...)

    • קלט/פלט

    • אונריים (+,-)

    • השוואה (==,=!,>,<,=>,...)

    • השמה

    • [],()

    • המרה

    • ++,--


3222618

העמסה של אופרטורים – סיכום (המשך)

  • הימנעו מהפתעות והטעיות:

    Stack s;

    s += 5; // prefer s.push(5);

    int x = s--; // prefer x = x.pop();

  • ספקו קבוצה שלמה של אופרטורים, שהקשר ביניהם טבעי:

    • למשל אם מימשתם "-" בינארי, אז הוסיפו גם את "=-", "-" אונרי.

    • ההגדרות צריכות להיות עקביות: x -= y שקול ל x = x-y.

  • הקפידו על שימוש חוזר בעת מימוש אופרטורים דומים:

    • למשל מימוש האופרטורים "+", "=+" יכול להתבסס אחד על השני (מי יתבסס על מי?).


3222618

העמסה של אופרטורים – סיכום (המשך)

  • מזערו את כמות האופרטורים להמרה:

    A::operator int() const {…}

    A::operator double() const {…}

    A a;

    cout << a; // compilation error

  • שמו לב לכך שבנאי המקבל כארגומנט טיפוס בסיסי יחיד הוא גם אופרטור המרה.

    Array a(10);

    a[3] = 5;

    a = 5; // Can be compiled but probably a wrong action.

    // a contents is lost.


3222618

העמסה של אופרטורים – סיכום (המשך)

  • הקומפיילר לא יבצע הרכבה של שתי המרות עקיפות.

    A::operator B() const {…}

    B::operator int() const {…}

    int calc(int x) {…}

    A a;

    calc(a); // compilation error

    B b(a); // converting A to B and using copy constructor

    calc(b); // converting B to int

    int x = b; // converting B to int and using assignment operator

    calc(x); // fine


3222618

העמסה של אופרטורים – סיכום (המשך)

  • אופרטור השייך למחלקה לעומת אופרטור גלובלי:

    • נעדיף אופרטור גלובלי לא חבר על פני אופרטור של מחלקה, בתנאי שמשתמשים אך ורק בפונקציות גישה בסיסיות (עקרון האנקפסולציה).

    • נעדיף להגדיר אופרטורים גלובליים לטיפול באפשרויות רבות על פני הגדרות של אופרטורים מקומיים רבים.

    • רצוי לא להגדיר לכל שדה פונקציית גישה (נוגד את עקרון ההסתרה) אלא לספק רק את אלו המשרתים את המחלקה.

    • כאשר לא נוח או לא יעיל להגדיר אופרטור גלובלי לא חבר אז נגדיר אותו כאופרטור של המחלקה (לעיתים "=+" הוא אופרטור כזה).


3222618

העמסה של אופרטורים – סיכום (המשך)

  • מקרים בהם נהיה מוכרחים להגדיר אופרטורים גלובליים:

    • הטיפוס של הארגומנט הראשון אינו מחלקה:

      Vector operator*( double, Vector );

    • הטיפוס של הארגומנט הראשון הוא מחלקה שלא ניתנת לשינוי:

      ostream& operator<<( ostream&, Vector );

    • נדרשת המרה של הארגומנט הראשון:

      Fraction operator*( Fraction, Fraction );

      Fraction b = 2*a;

  • גם ההיפך נכון, קיימים אופרטורים שלא ניתן להגדיר אותם כגלובליים (המרה=, [], () , ->, ).

  • קדימויות של אופרטורים: http://www.cppreference.com/wiki/operator_precedence


  • Login