פונקציות ומחלקות ידידות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-C++ של Coddy. שיעור 32 מתוך 104.
לפעמים פונקציה או מחלקה מחוץ למחלקה שלך זקוקה לגישה לחברים הפרטיים שלה. במקום להפוך את החברים האלה לציבוריים, C++ מספקת את מילת המפתח friend כדי להעניק גישה סלקטיבית תוך שמירה על הכימוס.
פונקציית ידיד מוכרזת בתוך מחלקה, אך אינה חברה במחלקה. היא יכולה לגשת לחברים הפרטיים והמוגנים של המחלקה:
class Box {
double width;
public:
Box(double w) : width(w) {}
// הכרז על printWidth כפונקציה ידידה
friend void printWidth(const Box& b);
};
// הגדרה של פונקציה ידידה - אינה פונקציית מחלקה
void printWidth(const Box& b) {
std::cout << b.width; // יכולה לגשת לחבר פרטי במחלקה
}שימו לב ש-printWidth מוגדרת מחוץ למחלקה ללא אופרטור פתרון תחום. זו פונקציה רגילה שיש לה במקרה הרשאות גישה מיוחדות.
אפשר גם להפוך מחלקה שלמה לידידה, וכך להעניק לכל פונקציות החבר שלה גישה לחברים הפרטיים:
class Engine {
int horsepower;
friend class Mechanic; // למחלקה Mechanic יש גישה לחברים פרטיים
public:
Engine(int hp) : horsepower(hp) {}
};
class Mechanic {
public:
void diagnose(const Engine& e) {
std::cout << e.horsepower; // מותר - Mechanic היא מחלקה ידידה
}
};חברות אינה הדדית — אם Engine מכריזה על Mechanic כחברה, Mechanic יכולה לגשת לחברים הפרטיים של Engine, אך לא להפך. השתמשו בחברות במשורה, מכיוון שהיא יוצרת צימוד הדוק בין מחלקות. היא שימושית במיוחד להעמסת אופרטורים ולפונקציות עזר קשורות.
אתגר
קלבואו נבנה מערכת מסרים מאובטחת, שבה למחלקה MessageReader דרושה גישה מיוחדת כדי לקרוא הודעות מוצפנות המאוחסנות במחלקה SecureMessage. זהו תרחיש מושלם לשימוש בפונקציות ובמחלקות ידידותיות.
תיצרו שני קבצים כדי לארגן את הקוד שלכם:
SecureMessage.h: הגדירו מחלקהSecureMessageשמאחסנת נתוני הודעה פרטיים ומעניקה גישה סלקטיבית לגורמים מהימנים. המחלקה שלכם צריכה לכלול:- חברים פרטיים:
content(מחרוזת) ו-secretKey(מספר שלם) - בנאי שמקבל את התוכן ואת המפתח הסודי
- הצהירו על
MessageReaderכמחלקה ידידותית כדי שתוכל לגשת לחברים פרטיים - הצהירו על פונקציה ידידותית
decryptMessage(const SecureMessage& msg, int key)שמחזירה את התוכן רק אם המפתח שסופק תואם למפתח הסודי, ובאחרת מחזירה"Access Denied"
לאחר מכן הגדירו את המחלקה
MessageReaderבאותו קובץ כותרת:- מתודה
readContent(const SecureMessage& msg)שניגשת ישירות ל-contentהפרטי של ההודעה ומחזירה אותו - מתודה
verifyKey(const SecureMessage& msg, int key)שבודקת אם המפתח שסופק תואם ל-secretKeyשל ההודעה ומחזירה"Valid"או"Invalid"
לבסוף, הגדירו את הפונקציה הידידותית
decryptMessageאחרי המחלקות.- חברים פרטיים:
main.cpp: הדגימו את פעולתן של פונקציות ידידותיות ומחלקות ידידותיות. קראו קלט של תוכן הודעה, מפתח סודי ומפתח בדיקה (כל אחד בשורה נפרדת), ואז:- צרו
SecureMessageעם התוכן והמפתח הסודי - צרו אובייקט
MessageReader - הדפיסו
"Reader access: <content>"באמצעות המתודהreadContent()של הקורא - הדפיסו
"Key verification: <result>"באמצעותverifyKey()עם מפתח הבדיקה - הדפיסו
"Decrypt with test key: <result>"באמצעות הפונקציה הידידותית עם מפתח הבדיקה - הדפיסו
"Decrypt with correct key: <result>"באמצעות הפונקציה הידידותית עם המפתח הסודי המקורי
- צרו
זכרו שפונקציות ידידותיות מוגדרות מחוץ למחלקה, ללא אופרטור תחימת תחום: הן פונקציות רגילות עם הרשאות גישה מיוחדות. המחלקה MessageReader, בהיותה מחלקה ידידותית, יכולה לגשת ישירות לחברים הפרטיים של SecureMessage בכל המתודות שלה.
המירו את קלטי המפתחות ממחרוזות למספרים שלמים באמצעות std::stoi(). כללו את קובץ הכותרת שלכם ב-main.cpp באמצעות #include "SecureMessage.h".
נסו בעצמכם
#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;
int main() {
// קרא את הקלט
string messageContent;
string secretKeyStr;
string testKeyStr;
getline(cin, messageContent);
getline(cin, secretKeyStr);
getline(cin, testKeyStr);
int secretKey = stoi(secretKeyStr);
int testKey = stoi(testKeyStr);
// TODO: צור SecureMessage עם התוכן והמפתח הסודי
// TODO: צור אובייקט MessageReader
// TODO: הדפס "Reader access: <content>" באמצעות readContent()
// TODO: הדפס "Key verification: <result>" באמצעות verifyKey() עם מפתח הבדיקה
// TODO: הדפס "Decrypt with test key: <result>" באמצעות decryptMessage עם מפתח הבדיקה
// TODO: הדפס "Decrypt with correct key: <result>" באמצעות decryptMessage עם המפתח הסודי
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++ אונליין