יום שלישי, 9 במאי 2017

חפיפת פונקציות וברירת מחדל


חפיפת פונקציות

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

-הפונקציות בעלות השם הזהה מקבלות מספר שונה של פרמטרים.
-הפונקציות בעלות השם הזהה מקבלות פרמטרים מסוגים שונים.
-הפונקציות בעלות השם הזהה מקבלות מספר שונה של פרמטרים, וגם מסוגים שונים.

דוגמה:
#include <iostream>
using namespace std;
 
int f1(){  //1
 return 5*3;
}
int f1(int xint y){ //2
 return x*y;
}
 
double f1(double xdouble y){  //3
 return x*y;
}
double f1(int xdouble yint z){  //4
 return x*y*z;
}
void main(){
 cout<<f1()<<"\n";         // 15
 cout<<f1(7,8)<<"\n";      // 56
 cout<<f1(7.5,8.5)<<"\n";  // 63.75
 cout<<f1(2,0.5,5)<<"\n";  // 5
 cout<<f1(2,5,6)<<"\n";    // 60
}




כפי שרואים בדוגמה, הקריאה הראשונה ל f1 הייתה בלי ערכים, לכן הופעלה f1 מספר 1 שלא מקבלת ערכים.
הקריאה השנייה ל f1 הייתה עם שני ערכים, לכן הופעלה f1 מספר 2 שמקבלת 2 ערכים.
הקריאה השלישית ל f1 הייתה עם שני ערכים ממשיים (=לא שלמים), לכן הופעלה f1 מספר 3 שמקבלת שני double.
הקריאה הרביעית ל f1 הייתה עם שלושה ערכים, לכן הופעלה f1 מספר 4 שמקבלת שלושה ערכים.
הקריאה החמישית ל f1 שוב הייתה עם שלושה ערכים. לכן שוב הופעלה f1 מספר 4.


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


דו משמעות

נתבונן בתוכנית הבאה ונראה שהמחשב לא יכול להחליט באיזו פונקציה להשתמש:
#include <iostream>
using namespace std;
 
double f2(float x){  //1
 return x*2;
}
double f2(double x){ //2
 return x*5;
}
 
void main(){
 cout<<f2(3)<<"\n";  /// שגיאה
}

שכן double ו float שניהם נועדו למספרים ממשיים, אז מי מהם יהיה יותר מתאים למספר שלם?
מכיוון שהם מתאימים באותו האופן לערך, זו שגיאת תיכנות.




התוכנית הבאה לא חוקית:
#include <iostream>
using namespace std;
 
double f2(double x){  //1
 return x*2;
}
 int f2(double x){ //2
 return x*5;
}
 
void main(){
 cout<<f2(3)<<"\n";  // שגיאה
}
התוכנית הזו לא חוקית משום שהפונקציות שתיהן מקבלות את אותו ערך.




יש מקרה מיוחד שאינו דו משמעות למרות שהיינו מצפים שכן יהיה כזה:
#include <iostream>
using namespace std;
 
double f2(float x){  //1
 return x*2;
}
double f2(double x){ //2
 return x*5;
}
 
void main(){
 cout<<f2(3.5)<<"\n";  // לא שגיאה
}
המקרה הזה אינו דו משמעות מהסיבה שהקומפיילר מתייחס לכל מספר ממשי כ double. לכן כאשר נתנו לפונקציה f2 את הערך 3.5 הקומפיילר חיפש פונקציה שמקבלת double, ומצא את פונקציה f2 מספר 2 כמתאימה.




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

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

לדוגמה:
#include <iostream>
using namespace std;
 
 int f(int xint y=5){ // הוא משתנה עם ברירת מחדל y
 return x*y;
}
 
void main(){
 cout<<f(3)<<"\n";  // ידפיס 15
 cout<<f(1,10)<<"\n";  // ידפיס 10
}
הכלל החשוב:
אם חלק מהפרמטרים הם עם ברירת מחדל, וחלק לא, כל אלו עם ברירת מחדל יהיו בצד ימין. וכל אלו בלי ברירת מחדל יהיו בצד שמאל.
כלומר:
 int f(int x, int y=5. int z=10){....}  חוקי
 int f(int x=3, int y. int z=10){....}  לא חוקי
 int f(int x, int y=5. int z){...........}  לא חוקי
 int f(int x=5, int y=10. int z){....}  לא חוקי






בעיית הדו משמעות שזה יכול ליצור:
נתבונן בתוכנית הבאה:
#include <iostream>
using namespace std;
 
double point(int a=0, int b=0){  //1
 return (a*a+b*b);
}
double point(int a){  //2
 return a*a;
 }
 
void main(){
 cout<<point()<<"\n";     // ידפיס 0
 cout<<point(3,5)<<"\n";  // ידפיס 34
 cout<<point(3)<<"\n";    // שגיאה
}
בקריאה הראשונה ברור שהפונקציה point מספר 1 תופעל.
בקריאה השנייה גם כן הפונקיה point מספר 1 תופעל.
אבל בקריאה השלישית הקומפיילר לא יודע איזו פונקציה להפעיל. לכן זו דו משמעות. שגיאה.







חפיפת פונקציות בונות

-אז למה שנרצה כמה פונקציות בעלות שם זהה? לאיזו תועלת?
זה נועד עבור הפונקציות הבונות. שחייבות להיות באותו שם (שם ה class). כדי ליצור מספר פונקציות בונות שונות נצטרך כאמור להבדיל ביניהן ע"י סוג/כמות הפרמטרים שהן מקבלות.


נתבונן בתוכנית הבאה:
#include <iostream>
#include <string.h>
using namespace std;
class worker {
 char name[20];
 int age;
public:
 worker() {
  cout<<"Enter name: ";
  cin >> name; 
  do {
   cout <<"Enter Age: ";
   cin >> age;
  }while(age < 18);
 }
 worker(char *nint a) {
  strcpy(name, n);
  if(a >= 18)
   age = a;
  else 
   age = 18;
 }
 void SetAge(int a) {
  if(a >= 18)
   age = a;
 }
 void Show() {
  cout<<"Name: "<<name << "  Age: "<<age<<"\n";
 }
};
void main() {
 worker w1("Shalom" , 27) , w2("Shlomo", 33),w3;//לא צריך לקבל ערכים ואפילו לא סוגרים ריקות. מפעיל את הפונקציה הבונה הראשונה w3 
 w1.Show();
 w2.Show();
 w3.Show();
}
שימו לב שלעולם תופעל רק פונקציה בונה אחת. לא משנה כמה פונקציות בונות יש ב class.








חפיפת פונקציות בונות עם ברירת מחדל

התבוננו בתוכנית ובפלט שלה:
#include <iostream>
using namespace std;
 
class point{
 int x,y;
public:
 point(int x1=0, int y1=0){
  x=x1;
  y=y1;
 }
 void show(){
  cout<<"("<<x<<","<<y<<")\n";
 }
};
void main(){
 point p1, p2(1), p3(3,3);
 p1.show();  // 0,0
 p2.show();  // 1,0
 p3.show();  // 3,3
}
בזכות ברירת המחדל, לא חייבים להעניק ערכים ביצירת אובייקט.
ב p1 לא הכנסנו שום ערך. במקרה הזה לא נדרשו אפילו סוגריים.







אין תגובות:

הוסף רשומת תגובה