Menu
Coddy logo textTech

תבניות מחלקה

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

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

template <typename T>
class Box {
    T value;
public:
    Box(T v) : value(v) {}
    T getValue() const { return value; }
    void setValue(T v) { value = v; }
};

int main() {
    Box<int> intBox(42);
    Box<std::string> strBox("Hello");
    
    std::cout << intBox.getValue() << std::endl;  // 42
    std::cout << strBox.getValue() << std::endl;  // שלום
}

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

לתבניות מחלקה יכולים להיות כמה פרמטרים של טיפוס, ואפילו פרמטרים שאינם טיפוסים, כמו מספרים שלמים:

template <typename T, int Size>
class FixedArray {
    T data[Size];
public:
    T& operator[](int index) { return data[index]; }
    int size() const { return Size; }
};

FixedArray<double, 5> arr;  // מערך של 5 ערכי double
arr[0] = 3.14;

כשמגדירים פונקציות חברות מחוץ למחלקה, יש לחזור על הצהרת התבנית:

template <typename T>
class Container {
    T* data;
public:
    Container();
    ~Container();
};

template <typename T>
Container<T>::Container() : data(nullptr) {}

template <typename T>
Container<T>::~Container() { delete data; }

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

challenge icon

אתגר

קל

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

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

  • Storage.h: הגדירו תבנית מחלקה בשם Storage שתשמש כמכל פשוט לערך יחיד עם כמה פעולות שימושיות:

    תבנית Storage שלכם צריכה לכלול:

    • חבר פרטי לאחסון הערך
    • בנאי שמקבל ערך התחלתי
    • מתודה getValue() שמחזירה את הערך המאוחסן
    • מתודה setValue() שמעדכנת את הערך המאוחסן
    • מתודה isEmpty() שמחזירה true אם הערך שווה לערך ברירת המחדל שנוצר עבור הטיפוס T, ו-false אחרת

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

    • חברים פרטיים לערך הראשון ולערך השני (שיכולים להיות מטיפוסים שונים)
    • בנאי שמאתחל את שני הערכים
    • מתודות getFirst() ו-getSecond() לקבלת כל אחד מהערכים
    • מתודה display() שמדפיסה: (<first>, <second>)
  • main.cpp: קראו ארבעה ערכים (כל אחד בשורה נפרדת):
    1. ערך שלם
    2. ערך שלם חדש
    3. ערך מחרוזת
    4. ערך מסוג double

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

    1. יצירת Storage<int> עם המספר השלם הראשון, והדפסת: Int storage: <value>
    2. עדכונו באמצעות המספר השלם השני בעזרת setValue(), ולאחר מכן הדפסת: Updated: <value>
    3. יצירת Storage<std::string> עם קלט המחרוזת, והדפסת: String storage: <value>
    4. בדיקה אם אחסון המחרוזת ריק, והדפסת: Is empty: <true/false> (הדפיסו true או false)
    5. יצירת Pair<std::string, double> עם ערכי המחרוזת וה-double, ולאחר מכן קריאה ל-display()
    6. יצירת Pair<int, int> עם שני המספרים השלמים שהתקבלו כקלט, ולאחר מכן קריאה ל-display()

לדוגמה, עם הקלטים 42, 100, Hello ו-3.14:

Int storage: 42
Updated: 100
String storage: Hello
Is empty: false
(Hello, 3.14)
(42, 100)

שימו לב שאותה תבנית Storage פועלת בצורה חלקה עם מספרים שלמים ומחרוזות, וש-Pair יכולה לשלב יחד טיפוסים שונים. המהדר יוצר הגדרות מחלקה נפרדות עבור כל צירוף טיפוסים שבו אתם משתמשים. זכרו להשתמש בתחביר template <typename T> עבור תבניות עם טיפוס יחיד, ובתחביר template <typename T, typename U> עבור תבניות עם כמה פרמטרים של טיפוס.

נסו בעצמכם

#include <iostream>
#include <string>
#include "Storage.h"

using namespace std;

int main() {
    // קרא את הקלטים
    int intValue1;
    int intValue2;
    string strValue;
    double doubleValue;
    
    cin >> intValue1;
    cin >> intValue2;
    cin >> strValue;
    cin >> doubleValue;
    
    // TODO: צור Storage<int> עם המספר השלם הראשון והדפס: Int storage: <value>
    
    // TODO: עדכן עם המספר השלם השני באמצעות setValue(), הדפס: Updated: <value>
    
    // TODO: צור Storage<std::string> עם קלט המחרוזת, הדפס: String storage: <value>
    
    // TODO: בדוק אם אחסון המחרוזת ריק, הדפס: Is empty: true/false
    
    // TODO: צור Pair<std::string, double> עם המחרוזת והמספר מסוג double, קרא ל-display()
    
    // TODO: צור Pair<int, int> עם שני המספרים השלמים, קרא ל-display()
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

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