Menu
Coddy logo textTech

std::function ו־std::bind

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

למרות שלמבדה הן כלי רב־עוצמה, לפעמים צריך לאחסן אובייקטים שניתנים לקריאה מסוגים שונים באופן אחיד, או להתאים פונקציות קיימות כך שיתאימו לחתימה נדרשת. std::function ו־std::bind מהכותרת <functional> פותרים את הבעיות האלה.

std::function היא מעטפת שמוחקת את הטיפוס ויכולה להכיל כל ישות שניתן לקרוא לה ותואמת לחתימה מסוימת — פונקציות, למבדות או אובייקטי פונקציה:

#include <iostream>
#include <functional>

int add(int a, int b) { return a + b; }

int main() {
    std::function<int(int, int)> operation;
    
    operation = add;                              // פונקציה רגילה
    std::cout << operation(3, 4) << "\n";        // 7
    
    operation = [](int a, int b) { return a * b; }; // למבדה
    std::cout << operation(3, 4) << "\n";        // 12
}

std::bind יוצרת אובייקט שניתן לקרוא לו על ידי קיבוע חלק מהארגומנטים של פונקציה קיימת. השתמשו ב-std::placeholders::_1, ב-_2 וכן הלאה כדי לסמן ארגומנטים שנותרים משתנים:

#include <iostream>
#include <functional>

void greet(const std::string& greeting, const std::string& name) {
    std::cout << greeting << ", " << name << "!\n";
}

int main() {
    using namespace std::placeholders;
    
    auto sayHello = std::bind(greet, "Hello", _1);
    sayHello("Alice");  // שלום, Alice!
    
    auto swapped = std::bind(greet, _2, _1);
    swapped("Bob", "Hi");  // היי, Bob!
}

הכלים האלה שימושיים במיוחד בתכנות מונחה עצמים לאחסון פונקציות callback כחברי מחלקה או למימוש תבנית האסטרטגיה. עם זאת, ב־C++ מודרנית לרוב מעדיפים ביטויי lambda על פני std::bind לשיפור הקריאות והביצועים — השתמשו ב־std::bind בעיקר כשצריך לשנות את סדר הארגומנטים או לעבוד עם קוד מדור קודם.

challenge icon

אתגר

קל

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

תארגן את הקוד שלך בשלושה קבצים:

  • MathOperations.h: הגדר אוסף של פונקציות מתמטיות עצמאיות שישמשו כספריית הפעולות שלך.

    צור את הפונקציות האלה:

    • add(int a, int b): מחזירה את הסכום
    • subtract(int a, int b): מחזירה את ההפרש (a - b)
    • multiply(int a, int b): מחזירה את המכפלה
    • power(int base, int exponent, int multiplier): מחזירה multiplier * (base ^ exponent). השתמש בלולאה פשוטה כדי לחשב את החזקה (נניח שהמעריכים אינם שליליים).
  • Calculator.h: הגדר מחלקה בשם Calculator שמשתמשת ב-std::function כדי לשמור פעולות ולהפעיל אותן באופן דינמי.

    ה-Calculator שלך צריך לכלול:

    • חבר פרטי std::function<int(int, int)> לשמירת הפעולה הבינארית הנוכחית
    • setOperation(std::function<int(int, int)> op): מגדירה את הפעולה הנוכחית
    • calculate(int a, int b): מפעילה את הפעולה השמורה ומחזירה את התוצאה

    כלול את הכותרות הדרושות (<functional>) וודא שאתה כולל את כותרת MathOperations שלך.

  • main.cpp: קרא שלוש קלטים:
    1. שם הפעולה: add, subtract, multiply או square
    2. המספר הראשון (מספר שלם)
    3. המספר השני (מספר שלם)

    צור Calculator והגדר אותו בהתאם לשם הפעולה:

    • עבור add, subtract ו-multiply: הקצה את הפונקציה המתאימה ישירות למחשבון
    • עבור square: השתמש ב-std::bind כדי ליצור גרסה מותאמת של power שתשתמש תמיד במעריך 2 ובמכפיל 1. הפונקציה הקשורה צריכה לקבל שני ארגומנטים, כאשר רק הראשון משמש כבסיס (אפשר להתעלם מהארגומנט השני באמצעות מציין מיקום).

    לאחר הגדרת הפעולה, קרא ל-calculate() עם שני המספרים שלך והדפס:

    Result: [value]

    לאחר מכן הדגם החלפה של פעולות באמצעות הקצאת lambda שמחזירה a + b + 100 למחשבון, הפעל אותה עם אותן קלטים והדפס:

    With bonus: [value]

לדוגמה, עם הקלטים add, 10 ו-5:

Result: 15
With bonus: 115

עם הקלטים multiply, 7 ו-3:

Result: 21
With bonus: 110

עם הקלטים square, 4 ו-0:

Result: 16
With bonus: 104

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

נסו בעצמכם

#include <iostream>
#include <string>
#include <functional>
#include "Calculator.h"

using namespace std;

int main() {
    // קראו את הקלט
    string operation;
    int num1, num2;
    cin >> operation >> num1 >> num2;
    
    // צרו מופע של Calculator
    Calculator calc;
    
    // TODO: הגדירו את המחשבון לפי שם הפעולה
    // עבור "add", "subtract", "multiply": הקצו ישירות את הפונקציה המתאימה
    // עבור "square": השתמשו ב-std::bind כדי ליצור גרסה מותאמת של power
    //   שמשתמשת תמיד במעריך 2 ובמכפיל 1
    //   רמז: השתמשו ב-std::placeholders::_1 עבור ארגומנט הבסיס
    
    if (operation == "add") {
        // הקוד שלכם כאן
    } else if (operation == "subtract") {
        // הקוד שלכם כאן
    } else if (operation == "multiply") {
        // הקוד שלכם כאן
    } else if (operation == "square") {
        // הקוד שלכם כאן - השתמשו ב-std::bind עם הפונקציה power
    }
    
    // TODO: קראו ל-calculate() והדפיסו את התוצאה
    // פורמט: "Result: [value]"
    
    // TODO: הדגימו החלפה בין פעולות
    // הקצה למחשבון פונקציית lambda שמחזירה a + b + 100
    // קרא שוב ל־calculate() והדפס את התוצאה
    // פורמט: "With bonus: [value]"
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

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