0Pricing
Go Academy · บทเรียน

ตัวรับพอยน์เตอร์ในเมธอด

เปลี่ยนแปลงสถานะของสตรักต์ผ่านตัวรับพอยน์เตอร์

ตัวรับพอยน์เตอร์ในเมธอด เป็นบทเรียน Go Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Go Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน

สรุปทบทวน: รีซีฟเวอร์แบบค่ากับรีซีฟเวอร์แบบพอยน์เตอร์

รีซีฟเวอร์แบบค่าทำงานกับสำเนาของโครงสร้าง ส่วนรีซีฟเวอร์แบบพอยน์เตอร์ทำงานกับต้นฉบับ ใช้รีซีฟเวอร์แบบพอยน์เตอร์ทุกครั้งที่เมธอดจำเป็นต้องแก้ไขสถานะ

การแก้ไขค่าด้วยรีซีฟเวอร์แบบพอยน์เตอร์

มีเพียงรีซีฟเวอร์แบบพอยน์เตอร์เท่านั้นที่สามารถแก้ไขโครงสร้างได้:

package main
import "fmt"

type Stack struct{ items []int }

func (s *Stack) Push(v int) { s.items = append(s.items, v) }
func (s *Stack) Pop() int {
    n := len(s.items) - 1
    v := s.items[n]
    s.items = s.items[:n]
    return v
}

func main() {
    var st Stack
    st.Push(1); st.Push(2)
    fmt.Println(st.Pop()) // 2
}

กฎความสอดคล้อง

หากมีเมธอดใดในชนิดข้อมูลที่ต้องใช้รีซีฟเวอร์แบบพอยน์เตอร์ เมธอดทั้งหมดก็ควรใช้รีซีฟเวอร์แบบพอยน์เตอร์ด้วย การผสมกันจะทำให้เกิดปัญหากับการรองรับอินเทอร์เฟซ:

package main
import "fmt"

type Counter struct{ n int }

func (c *Counter) Inc()   { c.n++ }   // pointer
func (c *Counter) Val() int { return c.n } // pointer too

func main() {
    c := &Counter{}
    c.Inc(); c.Inc()
    fmt.Println(c.Val()) // 2
}

การรองรับอินเทอร์เฟซ

เมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์จะอยู่ในชุดเมธอดของ *T เท่านั้น ไม่ใช่ของ T ซึ่งส่งผลต่อการรองรับอินเทอร์เฟซ:

package main
import "fmt"

type Writer interface{ Write([]byte) }

type File struct{ name string }
func (f *File) Write(b []byte) { fmt.Println(string(b)) }

func main() {
    var w Writer = &File{"out.txt"} // OK
    // var w Writer = File{} — compile error
    w.Write([]byte("hello"))
}

การรับที่อยู่โดยอัตโนมัติ

Go จะรับที่อยู่โดยอัตโนมัติเมื่อเรียกเมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์กับค่าที่สามารถระบุที่อยู่ได้:

package main
import "fmt"

type Box struct{ V int }
func (b *Box) Double() { b.V *= 2 }

func main() {
    b := Box{5}
    b.Double()          // Go does (&b).Double()
    fmt.Println(b.V)    // 10
}

ค่าที่ระบุที่อยู่ไม่ได้

คุณไม่สามารถเรียกเมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์กับค่าที่ระบุที่อยู่ไม่ได้ เช่น สมาชิกของแมปหรือค่าที่คืนจากฟังก์ชันซึ่งไม่ได้กำหนดให้กับตัวแปร:

package main

type T struct{ V int }
func (t *T) Set(v int) { t.V = v }

func getT() T { return T{} }

func main() {
    var t T
    t.Set(1)       // OK — t is addressable
    // getT().Set(1) — ERROR: cannot take address of getT()
    _ = t
}

รีซีฟเวอร์ของโครงสร้างขนาดใหญ่

ใช้รีซีฟเวอร์แบบพอยน์เตอร์กับโครงสร้างขนาดใหญ่เสมอ เพื่อหลีกเลี่ยงการคัดลอกที่ใช้ทรัพยากรมากในทุกครั้งที่เรียกเมธอด:

package main

type Image struct{ pixels [1024][1024]uint8 }

// Value receiver copies 1MB per call!
func (img Image) Bad() uint8 { return img.pixels[0][0] }

// Pointer receiver — zero copy
func (img *Image) Good() uint8 { return img.pixels[0][0] }

func main() { _ = Image{} }

รีซีฟเวอร์แบบพอยน์เตอร์ที่เป็น nil

เมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์สามารถจัดการกรณี nil ได้อย่างเหมาะสม:

package main
import "fmt"

type List struct{ head *node }
type node struct{ val int; next *node }

func (l *List) Len() int {
    if l == nil { return 0 }
    count := 0
    for n := l.head; n != nil; n = n.next { count++ }
    return count
}

func main() {
    var l *List
    fmt.Println(l.Len()) // 0
}

การฝังชนิดข้อมูลและรีซีฟเวอร์แบบพอยน์เตอร์

เมื่อฝังชนิดข้อมูลที่มีรีซีฟเวอร์แบบพอยน์เตอร์ โครงสร้างภายนอกก็ต้องใช้งานในรูปพอยน์เตอร์ด้วย จึงจะเข้าถึงเมธอดที่เลื่อนระดับขึ้นมาได้:

package main
import "fmt"

type Base struct{ V int }
func (b *Base) Set(v int) { b.V = v }

type Derived struct{ Base }

func main() {
    d := &Derived{}
    d.Set(42)           // promoted pointer-receiver method
    fmt.Println(d.V)    // 42
}

การคัดลอกมิวเทกซ์

อย่าคัดลอกโครงสร้างที่มี sync.Mutex — ใช้รีซีฟเวอร์แบบพอยน์เตอร์หรือส่งผ่านด้วยพอยน์เตอร์เสมอ การคัดลอกมิวเทกซ์ที่ถูกล็อกจะทำให้เกิดการหยุดรอไม่สิ้นสุด:

package main
import "sync"

type Safe struct {
    mu sync.Mutex
    v  int
}

// Correct: pointer receiver
func (s *Safe) Set(v int) {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.v = v
}

func main() { _ = Safe{} }

go vet ตรวจจับข้อผิดพลาด

เรียกใช้ go vet ./... เพื่อตรวจจับข้อผิดพลาดทั่วไปเกี่ยวกับรีซีฟเวอร์แบบพอยน์เตอร์ เช่น การคัดลอกมิวเทกซ์และล็อก คำสั่งนี้เป็นส่วนหนึ่งของกระบวนการสร้างด้วย go build และการตรวจสอบ CI

ตรวจสอบอย่างรวดเร็ว

เมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์อยู่ในชุดเมธอดของชนิดข้อมูลใด

สรุปทบทวน

ประเด็นสำคัญ:

  • รีซีฟเวอร์แบบพอยน์เตอร์แก้ไขค่าได้ ส่วนรีซีฟเวอร์แบบค่าแก้ไขไม่ได้
  • ให้เมธอดทั้งหมดของชนิดข้อมูลหนึ่งใช้รีซีฟเวอร์ชนิดเดียวกัน
  • มีเพียง *T เท่านั้นที่รองรับอินเทอร์เฟซซึ่งต้องใช้เมธอดแบบรีซีฟเวอร์พอยน์เตอร์
  • อย่าคัดลอกโครงสร้างที่มีมิวเทกซ์ — ใช้รีซีฟเวอร์แบบพอยน์เตอร์

โจทย์ฝึกปฏิบัติ

สร้างโครงสร้าง Queue พร้อมเมธอดแบบรีซีฟเวอร์พอยน์เตอร์ ได้แก่ Enqueue(int), Dequeue() (int, bool) และ Len() int โดยใช้สไลซ์ []int เป็นโครงสร้างข้อมูลเบื้องหลัง

คำถามที่พบบ่อย

บทเรียน “ตัวรับพอยน์เตอร์ในเมธอด” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ตัวรับพอยน์เตอร์ในเมธอด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Go Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ตัวรับพอยน์เตอร์ในเมธอด”

เปลี่ยนแปลงสถานะของสตรักต์ผ่านตัวรับพอยน์เตอร์ คุณปฏิบัติ Go Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Go Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Go Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “ตัวรับพอยน์เตอร์ในเมธอด” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Go Academy นี้ได้ไหม

ได้ บทเรียน Go Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. พอยน์เตอร์คืออะไร
  2. พอยน์เตอร์และฟังก์ชัน
  3. ตัวรับพอยน์เตอร์ในเมธอด
  4. new() และกรณีที่ควรใช้พอยน์เตอร์
← กลับไปที่ Go Academy