Menu
Coddy logo textTech

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

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 64 מתוך 104.

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

תבנית פונקציה משתמשת במילת המפתח template ואחריה פרמטרים של תבנית בתוך סוגריים זוויתיים:

template <typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    std::cout << maximum(5, 3) << std::endl;       // משתמש בגרסה מסוג int
    std::cout << maximum(3.14, 2.71) << std::endl; // משתמש בגרסה מסוג double
    std::cout << maximum('a', 'z') << std::endl;   // משתמש בגרסה מסוג char
}

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

std::cout << maximum<double>(5, 3.14) << std::endl;  // מאלץ שימוש בגרסת double

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

template <typename T, typename U>
void printPair(T first, U second) {
    std::cout << first << ", " << second << std::endl;
}

printPair(42, "hello");    // T=int, U=const char*
printPair(3.14, 100);      // T=double, U=int

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

challenge icon

אתגר

קל

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

תיצרו שני קבצים:

  • MathUtils.h: הגדירו אוסף של תבניות פונקציה שמבצעות פעולות נפוצות:

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

    clamp: פונקציית תבנית שמקבלת שלושה פרמטרים: ערך, גבול תחתון וגבול עליון. היא מחזירה את הערך כשהוא מוגבל לטווח (מחזירה את הגבול התחתון אם הערך קטן ממנו, את הגבול העליון אם הערך גדול ממנו, ואחרת מחזירה את הערך עצמו).

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

  • main.cpp: קראו שישה קלטים (כל אחד בשורה נפרדת):

    1. מספר שלם ראשון
    2. מספר שלם שני
    3. ערך מסוג double להגבלה לטווח
    4. גבול תחתון (double)
    5. גבול עליון (double)
    6. תו

    הדגימו את התבניות שלכם באמצעות:

    1. מציאת הקטן מבין שני המספרים השלמים והדפסת: Min of <a> and <b>: <result>
    2. מציאת הקטן מבין התווים 'm' ותו הקלט שלכם, והדפסת: Min of m and <char>: <result>
    3. הגבלת ערך ה-double לטווח והדפסת: Clamp <value> to [<low>, <high>]: <result>
    4. הגבלת המספר השלם הראשון לטווח [0, 100] והדפסת: Clamp <value> to [0, 100]: <result>
    5. החלפת שני המספרים השלמים והדפסת: After swapValues: <a>, <b>

לדוגמה, עם הקלטים 25, 10, 3.7, 1.0, 5.0 ו-z:

Min of 25 and 10: 10
Min of m and z: m
Clamp 3.7 to [1, 5]: 3.7
Clamp 25 to [0, 100]: 25
After swapValues: 10, 25

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

נסו בעצמכם

#include <iostream>
#include "MathUtils.h"
using namespace std;

int main() {
    // קרא את הקלטים
    int a, b;
    double value, low, high;
    char ch;
    
    cin >> a;
    cin >> b;
    cin >> value;
    cin >> low;
    cin >> high;
    cin >> ch;
    
    // TODO: השתמש בתבנית minimum כדי למצוא את המינימום מבין שני מספרים שלמים
    // הדפס: "Min of <a> and <b>: <result>"
    
    // TODO: השתמש בתבנית minimum כדי למצוא את המינימום מבין 'm' לבין תו הקלט
    // הדפס: "Min of m and <char>: <result>"
    
    // TODO: השתמש בתבנית clamp על הערך מסוג double
    // הדפס: "Clamp <value> to [<low>, <high>]: <result>"
    
    // TODO: השתמש בתבנית clamp על המספר השלם הראשון בטווח [0, 100]
    // הדפס: "Clamp <value> to [0, 100]: <result>"
    
    // TODO: השתמש בתבנית swapValues על שני המספרים השלמים
    // הדפס: "After swapValues: <a>, <b>"
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

תרגלו בעצמכם: קומפיילר C++ אונליין