מצביעים למבנים
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 20 מתוך 107.
בעבודה עם מבנים, לעיתים קרובות תשתמש במצביעים כדי להימנע מהעתקה של מבני נתונים גדולים וכדי לשנות את המבנה המקורי. מצביע למבנה מכיל את כתובת הזיכרון של מופע המבנה הזה.
אפשר ליצור מצביע למבנה באמצעות האופרטור & על מבנה קיים, או באמצעות שימוש ישיר באופרטור הכתובת עם מילולית של מבנה.
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
ptr := &p // מצביע ל-struct קיים
// או ליצור ישירות
ptr2 := &Person{Name: "Bob", Age: 25}Go מספקת קיצור דרך נוח לגישה לשדות של מבנה דרך מצביע. במקום לכתוב (*ptr).Name, אפשר פשוט לכתוב ptr.Name. Go מבטלת את ההפניה של המצביע באופן אוטומטי עבורך.
ptr := &Person{Name: "Alice", Age: 30}
// שניהם פועלים באותו אופן
fmt.Println((*ptr).Name) // ביטול הפניה מפורש
fmt.Println(ptr.Name) // ביטול הפניה אוטומטיביטול ההפניה האוטומטי הזה חל גם בעת שינוי שדות. שינויים דרך המצביע משפיעים על המבנה המקורי.
p := Person{Name: "Alice", Age: 30}
ptr := &p
ptr.Age = 31 // משנה את המקור
fmt.Println(p.Age) // 31מצביעים למבנים הם חיוניים בתבניות ה-OOP של Go. הם מאפשרים למתודות לשנות את מצב המקבל, מאפשרים להעביר מבנים גדולים לפונקציות ביעילות, ומהווים את הבסיס לבניית מבני נתונים מקושרים כמו עצים ורשימות.
אתגר
קלבואו נבנה מערכת לניהול חשבונות בנק שתדגים כיצד מצביעים למבנים מאפשרים לך לשנות את הנתונים המקוריים ביעילות. תיצור חשבון שאפשר לעדכן באמצעות פעולות על מצביעים.
תארגן את הקוד שלך בשני קבצים:
account.go: הגדר מבנהBankAccountעם השדותOwner(מחרוזת),Balance(float64) ו-AccountNumber(מחרוזת). צור פונקציה בשםNewAccountשמקבלת שם של בעל החשבון ומספר חשבון כפרמטרים ומחזירה מצביע למבנה חדש מסוגBankAccountעם יתרת פתיחה של 0. צור גם פונקציה בשםDepositשמקבלת מצביע*BankAccountוסכום, ואז מוסיפה את הסכום הזה ליתרת החשבון באמצעות המצביע.main.go: קרא את פרטי החשבון מהקלט, צור חשבון באמצעות פונקציית הבנאי שלך (שמחזירה מצביע), בצע הפקדה באמצעות המצביע והצג את פרטי החשבון כדי לוודא שהיתרה השתנתה.
הקלטים הבאים יסופקו:
- שורה 1: שם בעל החשבון
- שורה 2: מספר חשבון
- שורה 3: סכום ההפקדה (מספר עשרוני)
פורמט הפלט:
Account: [AccountNumber]
Owner: [Owner]
Balance: [Balance]לדוגמה, עבור John Smith, ACC-12345 ו-500.50, הפלט שלך צריך להיות:
Account: ACC-12345
Owner: John Smith
Balance: 500.50זכור שפונקציית NewAccount שלך צריכה להחזיר *BankAccount באמצעות האופרטור &, ושפונקציית Deposit יכולה לשנות את היתרה ישירות באמצעות המצביע, בעזרת ביטול ההפניה האוטומטי של Go (למשל, account.Balance במקום (*account).Balance).
נסו בעצמכם
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// קרא את שם בעל החשבון
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// קרא את מספר החשבון
accountNumber, _ := reader.ReadString('\n')
accountNumber = strings.TrimSpace(accountNumber)
// קרא את סכום ההפקדה
amountStr, _ := reader.ReadString('\n')
amountStr = strings.TrimSpace(amountStr)
amount, _ := strconv.ParseFloat(amountStr, 64)
// TODO: צור חשבון חדש באמצעות הפונקציה NewAccount (מחזירה מצביע)
// TODO: בצע הפקדה באמצעות הפונקציה Deposit עם המצביע לחשבון
// TODO: הדפס את פרטי החשבון בפורמט הנדרש:
// חשבון: [AccountNumber]
// בעל החשבון: [Owner]
// יתרה: [Balance]
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP ב-Go
קבצים חיצונייםסביבת העבודה והמודולים של Goחבילות וייבואשמות מיוצאים לעומת שמות לא מיוצאיםמבוא ל-OOP ב-GoStructs כמחלקותהגדרת מתודות עבור Structsמקבלי Pointer לעומת Valueאתחול Structפונקציות Constructorחזרה – מחשבון פשוט4ממשקים
מבוא לממשקיםמימוש משתמעממשק כחוזהממשק ריק (any)אימות טיפוסמתג טיפוסיםהרכבת ממשקיםממשקי Stringer ו-Errorסיכום – מחשבון צורות7כימוס
שדות מיוצאים לעומת שדות לא מיוצאיםכימוס ברמת החבילהמתודות Getter ו-Setterהסתרת מידע ב-Goסיכום – רשומות תלמידים10Generics (Go 1.18 ומעלה)
מבוא ל־Genericsפרמטרים של טיפוסיםאילוצי טיפוסיםמבנים גנרייםפתרון עוקף למתודות גנריותסיכום – אוסף גנרי2העמקה בסוגים וב-Structs
סוגים בסיסיים ומורכביםהגדרות של סוגים מותאמים אישיתתגיות StructStructs אנונימייםStructs מקונניםערכי אפס וברירות מחדלסיכום – ספר אנשי קשר5קומפוזיציה במקום ירושה
למה ב-Go אין ירושהיסודות הטמעת Structקידום מתודותהטמעת כמה Structsהטמעה לעומת אגרגציההסתרת מתודות מוטמעותסיכום – היררכיית עובדים8טיפול בשגיאות ו-OOP
ממשק errorסוגי שגיאות מותאמים אישיתעטיפת שגיאות (fmt.Errorf)שגיאות Sentinelerrors.Is() ו-errors.As()Panic, Defer ו-Recoverסיכום – מנתח קבצים11ספרייה סטנדרטית ותכנות מונחה עצמים
io.Reader ו-io.Writersort.Interfaceהממשק fmt.Stringerencoding/json עם מבניםהממשק http.Handlerסיכום – מודלים של REST API3מצביעים וזיכרון
יסודות המצביעים ב-Goמצביעים למבניםהעברה לפי ערך לעומת לפי הפניההפונקציה new()איסוף אשפה ב-Goסיכום - בונה רשימה מקושרת6פולימורפיזם ב-Go
פולימורפיזם באמצעות ממשקיםטיפוס ברווז ב-Goכללי התאמה לממשקאוספים פולימורפייםהזרקת תלויותסיכום - מעבד תשלומים9מקביליות ו-OOP
יסודות GoroutinesChannels ותקשורתChannel עם חוצץ לעומת ללא חוצץפקודת Selectsync.Mutex ו-sync.RWMutexsync.WaitGroupתכנון Struct בטוח לשימוש במקבילסיכום - מאגר עובדים12מושגים מתקדמים בתכנות מונחה עצמים
דפוס האפשרויות הפונקציונליותדפוס Builder ב-Goשרשור מתודותכינויי טיפוסים לעומת הגדרותיסודות Reflectionסקירה כללית של יצירת קודתרגלו בעצמכם: קומפיילר Go אונליין