Menu
Coddy logo textTech

סמנטיקת העברה ו־rvalues

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

ב-C++, כל ביטוי הוא lvalue (בעל זהות מתמשכת וניתן לקבל את כתובתו) או rvalue (זמני ועומד להיהרס). הבנת ההבחנה הזאת מאפשרת להשתמש בסמנטיקת העברה — אופטימיזציה רבת־עוצמה שמונעת העתקה מיותרת.

הפניה לערך rvalue, המוצהרת באמצעות &&, נקשרת במיוחד לאובייקטים זמניים. כך אפשר "לגנוב" משאבים מאובייקטים שעומדים להיעלם בכל מקרה:

#include <iostream>
#include <utility>

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : data(new int[s]), size(s) {
        std::cout << "Constructed\n";
    }
    
    // בנאי העברה - גוזל משאבים
    Buffer(Buffer&& other) noexcept 
        : data(other.data), size(other.size) {
        other.data = nullptr;  // משאיר את המקור במצב תקין
        other.size = 0;
        std::cout << "Moved\n";
    }
    
    ~Buffer() { delete[] data; }
};

int main() {
    Buffer b1(1000);
    Buffer b2(std::move(b1));  // מפעיל את בנאי ההעברה
}

הפונקציה std::move לא באמת מעבירה שום דבר — היא פשוט מבצעת המרה של lvalue להפניה ל-rvalue, ובכך מסמנת שאתה מוכן לוותר על המשאבים של האובייקט. ההעברה בפועל מתרחשת בבנאי ההעברה או באופרטור ההשמה בהעברה.

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

challenge icon

אתגר

קל

בואו נבנה מחלקת DataBuffer שמנהלת משאבים ומדגימה סמנטיקת העברה בפעולה. תוכלו לראות כיצד העברת משאבים במקום העתקתם יכולה לשפר משמעותית את היעילות בעת העברת בעלות על זיכרון שהוקצה באופן דינמי.

תארגנו את הקוד שלכם בשלושה קבצים:

  • DataBuffer.h: הגדירו מחלקת DataBuffer שמנהלת מערך של מספרים שלמים שהוקצה באופן דינמי.

    במחלקה שלכם צריכים להיות חברים פרטיים עבור מצביע הנתונים (int*), הגודל (size_t) והשם (std::string), כדי לעזור לעקוב אחר זהותם של המאגרים במהלך הפעולות.

    הצהירו על הדברים הבאים:

    • בנאי שמקבל שם מסוג std::string וגודל מסוג size_t, מקצה את המערך ומדפיס: [name] constructed with size [size]
    • בנאי העברה שמקבל הפניה לערך ימני, משתלט על המשאבים ומדפיס: [name] moved from [source_name] (כאשר המאגר שאליו מתבצעת ההעברה מקבל את שם המקור)
    • הורס שמדפיס [name] destroyed (או empty destroyed אם בוצעה העברה מהמאגר)
    • שיטת getSize() שמחזירה את הגודל הנוכחי
    • שיטת getName() שמחזירה את שם המאגר

    זכרו לסמן את בנאי ההעברה בתור noexcept ולהשאיר את אובייקט המקור במצב ריק תקין (nullptr, size 0, name "empty").

  • DataBuffer.cpp: ממשו את כל השיטות שהוצהרו בקובץ הכותרת. כאשר ההורס מופעל, מחקו את הנתונים רק אם המצביע אינו null. כללו את <iostream> עבור פלט.
  • main.cpp: קראו שני נתונים:
    1. שם עבור המאגר שלכם (מחרוזת)
    2. גודל עבור המאגר שלכם (מספר שלם)

    צרו DataBuffer עם השם והגודל הנתונים. לאחר מכן צרו מאגר שני באמצעות העברה מהראשון עם std::move(). אחרי ההעברה, הדפיסו את מצבם של שני המאגרים:

    • Original: [name] size=[size]
    • New: [name] size=[size]

    כללו את <utility> עבור std::move.

לדוגמה, עבור הקלטים Alpha ו-100:

Alpha constructed with size 100
Alpha moved from Alpha
Original: empty size=0
New: Alpha size=100
Alpha destroyed
empty destroyed

עבור הקלטים Buffer ו-50:

Buffer constructed with size 50
Buffer moved from Buffer
Original: empty size=0
New: Buffer size=50
Buffer destroyed
empty destroyed

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

נסו בעצמכם

#include <iostream>
#include <string>
#include <utility>
#include "DataBuffer.h"

int main() {
    std::string name;
    int size;
    
    std::cin >> name;
    std::cin >> size;
    
    // TODO: צרו DataBuffer עם השם והגודל הנתונים
    
    // TODO: צרו מאגר שני על ידי העברה מהראשון באמצעות std::move()
    
    // TODO: הדפיסו את המצב של שני המאגרים:
    // מקורי: [name] size=[size]
    // חדש: [name] size=[size]
    
    return 0;
}
quiz iconבחנו את עצמכם

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

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

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