מעטפת גנרית
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C של Coddy. שיעור 50 מתוך 61.
void* לבדו יכול לאחסן כל נתון, אבל יש לו פגם קריטי: מאבדים את המעקב אחר סוג הנתון שהוא מכיל. כדי לבנות מכלים גנריים שימושיים באמת, עלינו לשלב את מצביע הנתונים עם מידע על הטיפוס. כאן נכנסת לתמונה תבנית העטיפה הגנרית.
הרעיון פשוט: יוצרים struct שמאגד void* עם enum שמזהה את הטיפוס המאוחסן. כך, המעטפת תמיד יודעת איך לפרש את התוכן שלה.
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;
typedef struct {
void* data;
DataType type;
} Wrapper;בעת אחסון נתונים, מקצים זיכרון עבור הערך עצמו ומתעדים את סוגו. בעת שליפת הנתונים, בודקים את הסוג לפני המרה:
Wrapper wrap_int(int value) {
int* p = malloc(sizeof(int));
*p = value;
return (Wrapper){ .data = p, .type = TYPE_INT };
}
void print_wrapper(Wrapper* w) {
if (w->type == TYPE_INT) {
printf("%d\n", *(int*)w->data);
} else if (w->type == TYPE_STRING) {
printf("%s\n", (char*)w->data);
}
}התבנית הזאת היא הבסיס לבניית מכלים גנריים בטוחים מבחינת טיפוסים ב-C. ה-enum משמש כתג טיפוס בזמן ריצה, ומאפשר לקוד שלך לקבל החלטות בטוחות לגבי אופן הטיפול בנתונים המאוחסנים.
אתגר
קלבואו נבנה מערכת עטיפה גנרית ובטוחה מבחינת טיפוסים, שיכולה לאחסן מספר שלם או מחרוזת, ולעקוב אחר סוג הנתון שהיא מכילה כדי שנוכל לאחזר את הנתונים ולהדפיס אותם בבטחה בהמשך.
תארגנו את הקוד בשלושה קבצים:
wrapper.h: הגדירו טיפוס enum בשםDataTypeעם הערכיםTYPE_INTו-TYPE_STRING. לאחר מכן הגדירו מבנהWrapperשמאגד מצביע נתונים מסוגvoid*עם שדה מסוגDataType. הצהירו על אבות הטיפוס של הפונקציות ליצירת עטיפות ולהדפסת התוכן שלהן.wrapper.c: ממשו את הפונקציונליות של העטיפה:wrap_int: מקבלת מספר שלם, מקצה עבורו זיכרון, שומרת את הערך ומחזירה Wrapper עם תגית הסוג המתאימהwrap_string: מקבלת מחרוזת (char*), מקצה זיכרון ומעתיקה את המחרוזת, ומחזירה Wrapper עם תגית סוג המחרוזתprint_wrapper: בודקת את תגית הסוג ומדפיסה את הנתונים בהתאם (מספר שלם כפי שהוא, מחרוזת כפי שהיא)free_wrapper: משחררת את הנתונים שהוקצו דינמית בתוך העטיפה
main.c: חברו את הכול יחד. קראו סימון סוג (iעבור מספר שלם,sעבור מחרוזת) ולאחריו את הערך. צרו את העטיפה המתאימה, הדפיסו את תוכנה ושחררו את הזיכרון.
התוכנית שלכם תקבל שתי כניסות:
- סימון סוג:
iעבור מספר שלם אוsעבור מחרוזת - הערך לעטיפה
פלט לדוגמה כאשר הכניסות הן i ו-42:
42פלט לדוגמה כאשר הכניסות הן s ו-Hello:
Helloפלט לדוגמה כאשר הכניסות הן i ו--100:
-100פלט לדוגמה כאשר הכניסות הן s ו-Generic Programming:
Generic Programmingזכרו להשתמש בשומרי הכללה בקובץ הכותרת שלכם. בעת העתקת מחרוזת, הקצו מספיק מקום למחרוזת ולתו המסיים null באמצעות strlen ו-strcpy.
נסו בעצמכם
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // קלוט את תו השורה החדשה
Wrapper w;
if (type == 'i') {
int value;
scanf("%d", &value);
// TODO: צור מעטפת למספר שלם באמצעות wrap_int
} else if (type == 's') {
char str[256];
fgets(str, sizeof(str), stdin);
// הסר את תו השורה החדשה שבסוף, אם הוא קיים
str[strcspn(str, "\n")] = '\0';
// TODO: צור מעטפת למחרוזת באמצעות wrap_string
}
// TODO: הדפס את תוכן המעטפת באמצעות print_wrapper
// TODO: שחרר את הזיכרון של המעטפת באמצעות free_wrapper
return 0;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
4כימוס
המושג של מצביעים אטומיםהגדרת מבנים אטומיםפונקציות Get ו-Setאימות ב-Settersסיכום: הקופסה הסודית2אובייקטים ומתודות
מבנים כאובייקטיםמצביע 'Self'נכונות constמצביע לעומת ערךמתודות עזרחזרה: מנהל נקודות5פרויקט: חשבון בנק פשוט
הגדרת הפרויקטמימוש החשבוןתרגלו בעצמכם: קומפיילר C אונליין