מחלקות מקוננות ופנימיות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 38 מתוך 104.
מחלקה מקוננת היא מחלקה שמוגדרת בתוך מחלקה אחרת. הטכניקה הזו עוזרת לארגן קוד באמצעות קיבוץ של טיפוסים קשורים יחד, ויכולה לשפר את הכימוס על ידי הסתרת פרטי המימוש.
class LinkedList {
public:
class Node { // מחלקה מקוננת
int data;
Node* next;
public:
Node(int d) : data(d), next(nullptr) {}
friend class LinkedList;
};
private:
Node* head;
public:
LinkedList() : head(nullptr) {}
};המחלקה המקוננת Node קשורה קשר הדוק ל־LinkedList — היא הגיונית רק בהקשר הזה. באמצעות קינון שלה, אנחנו מסמנים את הקשר הזה ונמנעים מזיהום מרחב השמות הגלובלי בשם כללי כמו "Node".
מחלקות מקוננות כפופות לאותם כללי גישה שלמדת. מחלקה מקוננת שמוצהרת במקטע הפרטי נגישה רק מתוך המחלקה החיצונית.
כאשר היא מוכרזת כציבורית, קוד חיצוני יכול להשתמש בה באמצעות אופרטור התחום:
LinkedList::Node node(42); // רק אם Node היא publicפרט חשוב אחד לגבי כיווניות: מחלקה מקוננת היא חברה במחלקה העוטפת ויש לה אותן זכויות גישה כמו לכל חבר אחר, ולכן היא יכולה לגשת לחברים הפרטיים של המחלקה העוטפת ללא הצהרת friend. ההפך אינו אוטומטי, עם זאת: למחלקה העוטפת אין גישה לחברים הפרטיים של המחלקה המקוננת.
לכן Node מצהירה על friend class LinkedList; בדוגמה שלמעלה: הצהרת friend מעניקה למחלקה העוטפת LinkedList גישה לחברים הפרטיים של Node, הכיוון היחיד שאינו אוטומטי.
אתגר
קלבואו נבנה מערכת פשוטה לניהול משימות, שבה המשימות מאורגנות בתוך פרויקט. זהו מקרה מושלם למחלקות מקוננות: Task הגיונית רק בהקשר של Project, ולכן נקנן אותה בתוכה כדי לשמור על קוד מאורגן ולהימנע מזיהום מרחב השמות הגלובלי.
תיצרו שני קבצים כדי לארגן את הקוד:
Project.h: הגדירו מחלקה בשםProjectשמכילה מחלקה מקוננת בשםTask.יש להצהיר על המחלקה המקוננת
Taskבאזור public שלProject, כדי שיהיה אפשר לגשת אליה מבחוץ. עליה לאחסן שם של משימה (מחרוזת) ואת מצב ההשלמה שלה (bool). הוסיפו לה בנאי שמקבל את השם ומגדיר את המשימה כלא הושלמה כברירת מחדל. כללו את המתודותcomplete()לסימון המשימה כהושלמה,isComplete()לבדיקת מצבה, ו-getName()לקבלת השם.מחלקת
Projectעצמה צריכה לאחסן שם של פרויקט (מחרוזת) ואוסף של מצביעים ל-Task. ספקו בנאי שמקבל את שם הפרויקט, מתודהaddTask(const std::string& taskName)שיוצרת משימה חדשה ומוסיפה אותה לפרויקט, מתודהcompleteTask(const std::string& taskName)שמאתרת משימה לפי שמה ומשלימה אותה, ומתודהdisplayStatus()שמציגה את שם הפרויקט ולאחריו את המצב של כל משימה.מכיוון ש-
Taskמקוננת בתוךProject, המחלקהProjectיכולה לעבוד ישירות עם משימות. אל תשכחו לשחרר את המשימות שהוקצו באופן דינמי בתוך ההורס.main.cpp: חברו את כל החלקים על ידי קריאת שם של פרויקט ושני שמות של משימות מהקלט (שלוש שורות בסך הכול). צרוProjectעם השם הנתון, הוסיפו אליו את שתי המשימות, ואז הציגו את המצב ההתחלתי. השלימו את המשימה הראשונה והציגו שוב את המצב כדי להראות את השינוי.פורמט הפלט של
displayStatus()צריך להיות:Project: <project_name> - <task_name>: [incomplete] - <task_name>: [complete]השתמשו ב-
[complete]או ב-[incomplete]בהתאם למצב של כל משימה.
האתגר הזה מדגים כיצד מחלקות מקוננות עוזרות לארגן טיפוסים קשורים: המחלקה Task קשורה באופן הדוק ל-Project, ולא הגיוני להשתמש בה בפני עצמה. קינון המחלקה מסמן בבירור את הקשר הזה במבנה הקוד שלנו.
נסו בעצמכם
#include <iostream>
#include <string>
#include "Project.h"
using namespace std;
int main() {
// קרא את הקלט
string projectName;
string task1Name;
string task2Name;
getline(cin, projectName);
getline(cin, task1Name);
getline(cin, task2Name);
// TODO: צור Project עם השם הנתון
// TODO: הוסף את שתי המשימות לפרויקט
// TODO: הצג את המצב ההתחלתי
// TODO: השלם את המשימה הראשונה
// TODO: הצג שוב את המצב כדי להראות את השינוי
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++ אונליין