Menu
Coddy logo textTech

אידיום Pimpl

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

אידיומת Pimpl (מצביע למימוש) היא טכניקה שמסתירה את פרטי המימוש של מחלקה על ידי העברתם למחלקה נפרדת שהוצהרה מראש. כך מצטמצמות התלויות בקומפילציה, והחברים הפרטיים נשארים מוסתרים באמת מקובץ הכותרת.

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

// Widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();
    void doSomething();
    
private:
    class Impl;  // הצהרה מקדימה
    std::unique_ptr<Impl> pImpl;
};
// Widget.cpp
#include "Widget.h"
#include <iostream>

class Widget::Impl {
public:
    int data = 42;
    void process() { std::cout << "Processing: " << data << "\n"; }
};

Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;

void Widget::doSomething() { pImpl->process(); }

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

שימו לב שיש להגדיר את ההורס בקובץ המקור (גם אם הוא מוגדר כברירת מחדל), כי unique_ptr זקוק לטיפוס המלא של Impl כדי למחוק אותו. זוהי מלכודת נפוצה בשימוש ראשוני ב־Pimpl.

challenge icon

אתגר

קל

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

תארגן את הקוד שלך בשלושה קבצים:

  • SecureMessage.h: הגדר את הממשק הציבורי של המחלקה SecureMessage שלך.

    המחלקה שלך צריכה לכלול מחלקת Impl שהוכרזה בהכרזה מקדימה ו-std::unique_ptr אליה. הממשק הציבורי צריך לכלול:

    • בנאי שמקבל const std::string& עבור ההודעה המקורית
    • הורס (חובה להצהיר עליו כאן ולהגדיר אותו בקובץ ‎.cpp)
    • מתודת setKey(int key) להגדרת מפתח הצפנה
    • מתודת getEncrypted() שמחזירה את ההודעה המוצפנת בתור std::string
    • מתודת getOriginal() שמחזירה את ההודעה המקורית

    קובץ הכותרת צריך להציג רק את הממשק הציבורי: פרטי המימוש של אופן פעולת ההצפנה לא צריכים להופיע בו.

  • SecureMessage.cpp: הגדר את המחלקה המקוננת Impl וממש את כל המתודות.

    המחלקה Impl שלך צריכה לאחסן את ההודעה המקורית ואת מפתח ההצפנה (ערך ברירת המחדל הוא 0), ולטפל בלוגיקת ההצפנה בפועל. השתמש בצופן קיסר פשוט להצפנה: הזז כל תו לפי ערך המפתח. לדוגמה, עם מפתח 3, 'a' הופך ל-'d', ו-'z' חוזר להתחלה והופך ל-'c'.

    ההצפנה צריכה להשפיע רק על אותיות קטנות (a-z), ולהשאיר את כל התווים האחרים ללא שינוי. זכור להגדיר כאן את ההורס (גם אם הוא מוגדר כברירת מחדל), מכיוון ש-unique_ptr זקוק לטיפוס Impl מלא.

  • main.cpp: קרא שני קלטים:
    1. מחרוזת הודעה (עשויה להכיל רווחים)
    2. מפתח הצפנה (מספר שלם)

    צור אובייקט SecureMessage, הגדר את המפתח והצג את התוצאות:

    1. הדפס Original: ואחריו ההודעה המקורית
    2. הדפס Encrypted: ואחריו ההודעה המוצפנת

לדוגמה, עם הקלטים hello world ו-3:

Original: hello world
Encrypted: khoor zruog

עם הקלטים xyz abc ו-5:

Original: xyz abc
Encrypted: cde fgh

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

נסו בעצמכם

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

int main() {
    // קרא את ההודעה (עשויה להכיל רווחים)
    std::string message;
    std::getline(std::cin, message);
    
    // קרא את מפתח ההצפנה
    int key;
    std::cin >> key;
    
    // TODO: צור אובייקט SecureMessage עם ההודעה
    
    // TODO: הגדר את מפתח ההצפנה
    
    // TODO: הדפס "Original: " ולאחר מכן את ההודעה המקורית
    
    // TODO: הדפס "Encrypted: " ולאחר מכן את ההודעה המוצפנת
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

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