התמחות תבניות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 66 מתוך 104.
לפעמים תבנית כללית אינה מתאימה לכל סוג. לדוגמה, השוואת מחרוזות בסגנון C באמצעות > משווה כתובות של מצביעים, ולא את הטקסט עצמו. התמחות תבניות מאפשרת לך לספק מימוש מותאם אישית לסוגים מסוימים, תוך שמירה על הגרסה הכללית לכל השאר.
התמחות מלאה מחליפה את התבנית כולה עבור טיפוס מסוים אחד. מצהירים עליה באמצעות רשימת פרמטרי תבנית ריקה ומציינים את הטיפוס הקונקרטי:
// תבנית ראשית
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// התמחות מלאה עבור const char*
template <>
class Printer<const char*> {
public:
void print(const char* value) {
std::cout << "String: " << value << std::endl;
}
};
Printer<int> intPrinter;
intPrinter.print(42); // פלט: 42
Printer<const char*> strPrinter;
strPrinter.print("hello"); // פלט: String: helloאפשר גם ליצור התמחות לתבניות פונקציה:
template <typename T>
bool isEqual(T a, T b) {
return a == b;
}
template <>
bool isEqual<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) == 0;
}
isEqual(5, 5); // משתמש בגרסה הגנרית
isEqual("hi", "hi"); // משתמש בגרסה המתמחההמהדר תמיד מעדיף את ההתאמה הספציפית ביותר - אם קיימת התמחות עבור הטיפוס המדויק שבו נעשה שימוש, היא תיבחר במקום התבנית הגנרית.
אתגר
קלנבנה מערכת עיצוב מודעת-סוג, שמדגימה כיצד התמחות של תבניות מאפשרת להתאים אישית את ההתנהגות עבור סוגים מסוימים, תוך שמירה על ברירת מחדל כללית לכל השאר.
תיצרו שני קבצים כדי לארגן את קוד התבניות שלכם:
Formatter.h: הגדירו תבנית מחלקה בשםFormatterשמעצבת ערכים להצגה. התבנית הכללית שלכם צריכה לעבוד עם כל סוג, אך תיצרו גם גרסאות מותאמות לסוגים שדורשים טיפול מיוחד.תבנית
Formatterהראשית צריכה לכלול:- מתודה
format()שמקבלת ערך מסוג T ומדפיסה:Value: <value>
צרו התמחות מלאה עבור
boolשמדפיסה טקסט קריא לבני אדם במקום 1 או 0:- המתודה
format()צריכה להדפיס:Boolean: trueאוBoolean: false
צרו התמחות מלאה עבור
const char*שמוסיפה מרכאות מסביב למחרוזות:- המתודה
format()צריכה להדפיס:String: "<value>"
צרו גם תבנית פונקציה בשם
formatPairשמקבלת שני ערכים מאותו סוג ומדפיסה אותם יחד כך:Pair: [<first>, <second>]צרו התמחות של
formatPairעבורconst char*שעוטפת כל מחרוזת במרכאות:Pair: ["<first>", "<second>"]- מתודה
main.cpp: קראו ארבעה קלטים (כל אחד בשורה נפרדת):- מספר שלם
- מספר ממשי
- ערך בוליאני כמחרוזת (
trueאוfalse) - ערך מחרוזת
הדגימו את מעצבי הפורמט שלכם על ידי יצירת אובייקטים מתאימים של
Formatterוקריאה ל-format()עבור כל סוג:- עצבו את המספר השלם באמצעות
Formatter<int> - עצבו את המספר הממשי באמצעות
Formatter<double> - עצבו את הערך הבוליאני באמצעות
Formatter<bool>(תחילה המירו את קלט המחרוזת לערך בוליאני) - עצבו את המחרוזת באמצעות
Formatter<const char*>
לאחר מכן הדגימו את ההתמחות של תבנית הפונקציה:
- קראו ל-
formatPairעם שני מספרים שלמים: 10 ו-20 - קראו ל-
formatPairעם שתי מחרוזות C:"hello"ו-"world"
לדוגמה, עם הקלטים 42, 3.14, true ו-Hello:
Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]שימו לב כיצד התבנית הכללית מטפלת במספרים שלמים ובמספרים ממשיים באופן זהה, בעוד שהגרסאות המותאמות עבור bool ו-const char* מספקות עיצוב מותאם אישית. המהדר בוחר אוטומטית את ההתאמה הספציפית ביותר עבור כל סוג שבו משתמשים.
נסו בעצמכם
#include <iostream>
#include <string>
#include "Formatter.h"
using namespace std;
int main() {
// קראו את ערכי הקלט
int intVal;
double doubleVal;
string boolStr;
string strVal;
cin >> intVal;
cin >> doubleVal;
cin >> boolStr;
cin >> strVal;
// המירו את המחרוזת ל-bool
bool boolVal = (boolStr == "true");
// TODO: צרו Formatter<int> ועצבו את המספר השלם
// TODO: צרו Formatter<double> ועצבו את המספר מסוג double
// TODO: צרו Formatter<bool> ועצבו את הערך הבוליאני
// TODO: צרו Formatter<const char*> ועצבו את המחרוזת
// רמז: השתמשו ב-strVal.c_str() כדי לקבל const char*
// TODO: קראו ל-formatPair עם שני מספרים שלמים: 10 ו-20
// TODO: קראו ל-formatPair עם שתי מחרוזות C: "hello" ו-"world"
return 0;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP
קבצים חיצונייםבנייה וקומפילציה של C++קובצי Header וקובצי מקורמרחבי שמות והיקףמבוא ל-OOP ב-C++מחלקות לעומת אובייקטיםמצביע thisמתודות (פונקציות איבר)תכונות (חברי נתונים)יסודות הבנאים וההורסיםסיכום – מחשבון פשוט4מאפייני מחלקה
חברי מופע לעומת חברים סטטייםגטרים וסטריםפונקציות איבר קבועותמילת המפתח mutableמתודות ומשתנים סטטייםפונקציות ומחלקות ידידותסיכום – מנהל חשבונות בנק7ירושה
ירושה בסיסיתרמות גישה בירושהסדר הקריאות לבנאי ולמפרקדריסת מתודותפונקציות וירטואליות ו־VTableירושה מרובהירושה וירטואליתסיכום – היררכיית עובדים2ניהול זיכרון
זיכרון Stack לעומת Heapמצביעים והפניותזיכרון דינמי (new/delete)מצביעים חכמים ב-C++RAII ב-C++סיכום – מנהל מערכים דינמיים5כימוס
מצייני גישה ב-C++מצייני גישה לעומקהסתרת מידעמבנה לעומת מחלקהמחלקות מקוננות ופנימיותסיכום – מערכת רשומות תלמידים8רב־צורתיות
רב־צורתיות בזמן קומפילציה לעומת בזמן ריצההעמסת פונקציותפונקציות וירטואליות — מבט נוסףפונקציות וירטואליות טהורותמחלקות מופשטותתכנון ממשקים ב־C++המרה דינמית ו־RTTIסיכום — מחשבון צורות3בנאים והורסים
בנאי ברירת מחדלבנאי עם פרמטריםבנאי העתקהבנאי העברהרשימות אתחול בבנאיםבנאים מאציליםמעמיקים בנושא הורסיםכלל השלושה / החמישה / האפססיכום – מחלקת String6העמסת אופרטורים
מבוא להעמסת אופרטוריםהעמסת אופרטורים אריתמטייםהעמסת אופרטורי השוואהאופרטורי זרםהעמסת אופרטור השמההעמסת האופרטורים [] ו־()אופרטורים להמרת טיפוסיםסיכום – מחלקת Matrix9תבניות
תבניות פונקציהתבניות מחלקההתמחות תבניותתבניות ארגומנטים משתנותיסודות SFINAE ותכונות טיפוסיםסיכום – מכל גנריתרגלו בעצמכם: קומפיילר C++ אונליין