1.9 - מודל מחלקות ופולימורפיזם - פתרון
פתרון - חוזה הכתיבה נשאר משותף¶
הקוד הבא הוא תוכנית מלאה של המימוש הסופר:
#include <cassert>
#include <cstddef>
#include <cstdio>
#include <memory>
#include <utility>
#include <string_view>
struct Sink {
virtual void write(std::string_view text) = 0;
virtual ~Sink() { std::puts("base destroyed"); }
};
struct CountingSink final : Sink {
std::size_t bytes = 0;
void write(std::string_view text) override {
bytes += text.size();
}
~CountingSink() override {
std::puts("counter destroyed");
}
};
void send_two(Sink& sink) {
sink.write("abc");
sink.write("de");
}
int main() {
auto concrete = std::make_unique<CountingSink>();
send_two(*concrete);
assert(concrete->bytes == 5);
std::unique_ptr<Sink> base = std::move(concrete);
assert(!concrete);
}
הקוד מכליל במפורש את ה־header של std::move, כך שאינו תלוי בהכללות עקיפות של המימוש. הבדיקה של חמש bytes מתבצעת כשהטיפוס הקונקרטי עדיין נגיש, ואחר כך הבעלות מועברת לבסיס.
ה־destructor של CountingSink רץ לפני destructor הבסיס. אילו היו למחלקה הנגזרת שדות בעלי destructorים, גם הם היו משתתפים בסדר ההשמדה המתאים. הטיפוס unique_ptr אינו פותר בעצמו destructor בסיס לא וירטואלי במקרה הזה; הוא מפעיל את מנגנון המחיקה של הטיפוס שהוגדר לו.
מה נשמר אחרי write¶
המימוש שומר מספר, ולא pointer לתווי ההודעה. לכן מותר לקורא להעביר תצוגה זמנית שתוויה חיים רק במשך הקריאה. אם רוצים לשמור את תוכן ההודעה, צריך להעתיק אותו לבעלים מתאים או לדרוש חוזה lifetime אחר בממשק.
פירוש הכתובות¶
ה־pointer אל Right עשוי להיות שונה מהכתובת של Both. ההמרה בוצעה על ידי השפה כדי להגיע לתת־אובייקט המתאים. אין להסיק שהאובייקט הועתק או שיש כאן שתי הקצאות.
הגודל וה־offset המדויקים תלויים ב־ABI. תוצאת הניסוי מסבירה את הbuild שנבדק, ואילו ההבטחה שמותר להמיר מצביע אל בסיס נגיש נובעת מכללי השפה.
בחירה בין מודלים¶
ב־variant נגדיר את רשימת סוגי היעד ונכתוב visitor שמבצע write לכל אפשרות. הוספת סוג שלישי משנה את רשימת החלופות ואת הטיפול שצריך להיות מלא עבורן.
בירושה אפשר להוסיף מחלקה חדשה שמממשת את אותו ממשק בלי לשנות את send_two. מצד שני, הרחבת הממשק בפעולה חדשה עלולה לדרוש שינוי בכל המימושים. זאת החלטה על ציר ההרחבה של המערכת, ולא תחרות תחבירית בין מילים בשפה.